{"id":"1a8af885-df9d-4e07-9d6e-9b5f223dc8a4","slug":"agentic-qa-boilerplate","kind":"skill","name":"agentic-qa-boilerplate","summary":"Agentic QA boilerplate built on Playwright + KATA + TypeScript. Multi-agent skills following the agentskills.io spec, orchestrating the full QA lifecycle: shift-left planning, in-sprint testing, TMS d","owner":"upex-galaxy","repoUrl":"https://github.com/upex-galaxy/agentic-qa-boilerplate","license":"MIT","licenseTier":"permissive","trust":"community","stars":26,"match":null,"fewStars":false,"format":"skill","createdAt":null,"addedAt":"2026-10-14T06:06:24.114Z","logoUrl":"https://avatars.githubusercontent.com/u/108208068?v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["developer-tools","browser-web","writing"],"tags":["agentic-workflow","agentskills","ai-agents","boilerplate","claude-code","jira-xray","playwright","qa-automation"],"runtime":"binary","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-14T06:44:07.826Z","sample":false,"url":"https://conduits.dev/tools/agentic-qa-boilerplate","language":"HTML","transports":["stdio"],"install":{},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"<div align=\"center\">\n\n<pre>\n                  ░█████  ░██████ ░███████░███   ░██░████████░██ ░██████                         \n                 ░██  ░██░██      ░██     ░████  ░██   ░██   ░██░██                              \n                 ░███████░██  ░███░█████  ░██░██ ░██   ░██   ░██░██                              \n  ██████████     ░██  ░██░██   ░██░██     ░██ ░██░██   ░██   ░██░██                              \n  ██▀▀▀▀▀▀██     ░██  ░██ ░██████ ░███████░██  ░████   ░██   ░██ ░██████                         \n  ██ ◉  ◉ ██     ░░   ░░  ░░░░░░  ░░░░░░░ ░░   ░░░░    ░░    ░░  ░░░░░░                          \n  ██   3  ██                                                                                     \n  ██████████     ░███████░███   ░██ ░██████ ░██░███   ░██░███████░███████░██████                 \n   ██    ██      ░██     ░████  ░██░██      ░██░████  ░██░██     ░██     ░██  ░██                \n                 ░█████  ░██░██ ░██░██  ░███░██░██░██ ░██░█████  ░█████  ░██████                 \n                 ░██     ░██ ░██░██░██   ░██░██░██ ░██░██░██     ░██     ░██  ░██                \n                 ░███████░██  ░████ ░██████ ░██░██  ░████░███████░███████░██  ░██                \n                 ░░░░░░░ ░░   ░░░░  ░░░░░░  ░░ ░░   ░░░░ ░░░░░░░ ░░░░░░░ ░░   ░░                 \n                               Quality Assurance Engineer                                        \n</pre>\n\n<h3>The QA workflow, but AI runs it.</h3>\n\n<p><i>From test plan to regression suite to release sign-off. Built for real QA teams shipping real test cases — every phase has a skill. You decide what to verify.</i></p>\n\n<br />\n\n[![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?style=for-the-badge&logo=typescript&logoColor=white)](https://www.typescriptlang.org/)\n[![Bun](https://img.shields.io/badge/Bun-000000?style=for-the-badge&logo=bun&logoColor=white)](https://bun.sh/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-EAB308?style=for-the-badge)](https://opensource.org/licenses/MIT)\n\n</div>\n\n<br />\n<br />\n\n<div align=\"center\">\n\n### Get started in one command\n\n</div>\n\n```bash\nbunx create-agentic-qa@latest <your-repo-name>\n```\n\n<div align=\"center\">\n\n<sub><b>One command.</b> Downloads · scrubs git history · renames the project · runs <code>bun install</code> · launches the installer, which opens a local page in your browser.</sub>\n\n</div>\n\n<br />\n<br />\n\n## Prerequisites\n\nBefore running `bunx create-agentic-qa@latest` or `bun install && bun setup`, install the **hard blockers**. The installer's preflight checks everything else in seconds, before anything is written, and shows the exact install command or URL for whatever is missing — but front-loading these saves a fail-and-retry loop.\n\n### Hard blockers (the preflight stops the run until they are fixed)\n\n| Tool                                                                                                                   | Min version | Why                                                                                                         | Install                                                                                |\n| ---------------------------------------------------------------------------------------------------------------------- | ----------- | ----------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |\n| **Bun**                                                                                                                | the floor `cli/preflight.ts` enforces | Runtime for every script (`bun install`, `bun setup`, `bun run test`, `bun xray`, `bun run setup:doctor`) | macOS/Linux/WSL: `curl -fsSL https://bun.sh/install \\| bash` · Windows: `powershell -c \"irm bun.sh/install.ps1 \\| iex\"` · [docs](https://bun.sh/docs/installation) |\n| **Node**                                                                                                               | the floor `cli/preflight.ts` enforces | Hooks run `node .agents/hooks/personality-reinject.mjs`; checked by the scaffolder doctor and the `bun setup` preflight | [nodejs.org](https://nodejs.org)                                                       |\n| **An agent** — [Claude Code](https://docs.claude.com/en/docs/claude-code), [OpenCode](https://opencode.ai/docs) **or** [Codex](https://developers.openai.com/codex/) | latest      | the `bun setup` preflight detects all three (`~/.claude/` or `claude` on PATH · `~/.config/opencode/` or `opencode` on PATH · `codex` on PATH or `.codex/config.toml` in the repo) and blocks only if none is found; the Team phase then lets you pick which to configure | See each project's official docs                           |\n| `git`                                                                                                                  | any         | Scaffolder runs `git init`; pre-commit hooks (Husky) require git                                            | [git-scm.com/downloads](https://git-scm.com/downloads)                                 |\n| `tar`                                                                                                                  | any         | Scaffolder extracts the template tarball. Either flavour works — GNU tar (Linux, WSL, Git Bash) or bsdtar   | Ships with macOS, Linux, and Windows 10 1803+ / Windows 11 (`C:\\Windows\\System32\\tar.exe`) |\n\n> **Windows**: PowerShell and cmd are supported — no WSL or Git Bash required. Install Bun with the PowerShell one-liner above rather than `npm i -g bun`, which writes only a `bun.cmd` shim.\n\n### Quasi-required (the preflight warns, the run goes on)\n\n| Tool          | Min version | Why                                                                                                                                                                                                       | Install                                                                                                                                                                            |\n| ------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **engram** | `MIN_ENGRAM_VERSION` in `cli/install.ts` | Persistent memory across sessions. The installer wires it into each selected agent with `engram setup`; on Claude Code it then offers the Engram plugin (`claude plugin install engram@engram`) for the session hooks. Framework still runs without it, but cross-session memory is off. The boilerplate does not use gentle-ai's workflow layer: every shipped workflow skill runs self-contained. | Homebrew (macOS / Linux): `brew trust gentleman-programming/tap && brew install gentleman-programming/tap/engram` · Go: `go install github.com/Gentleman-Programming/engram/v3/cmd/engram@latest` · [install docs](https://github.com/Gentleman-Programming/engram/blob/main/docs/INSTALLATION.md) |\n\n### Per-skill CLIs (lazy-required — needed when the skill runs, not at setup)\n\nThese are **not optional** for the workflow — each one is required by a specific skill. They are non-blocking at setup time because the installer cannot guess which skills you will actually use. Install them up front if you plan to use the whole stack, or lazily when the skill that uses them surfaces a missing-binary error.\n\nThe list is `EXTERNAL_CLIS` in `cli/install.ts`: each entry names the skill that needs it, a cross-platform install hint when one exists, and the official docs URL. `bun setup` shows that table with a found / missing status per tool, and `bun run setup:doctor` re-checks it any time. Repo search (`rg`) is not on it: Claude Code bundles its own, OpenCode and Codex use the system binary (`brew install ripgrep` · `apt install ripgrep` · `winget install BurntSushi.ripgrep.MSVC`).\n\n### Variables and MCP credentials (`.env` keys)\n\nEach harness has its own MCP config — `.mcp.json` (Claude Code), `opencode.jsonc` (OpenCode), `.codex/config.toml` (Codex) — and all three start every server that needs `.env` values through the same `.env` loader, which reads the file itself. Every variable the repo knows carries a scope in `cli/lib/variables-manifest.ts` (the source of truth; human guide: `docs/core/variables-de-entorno.html`):\n\n- **framework** (`core`): what the boilerplate reads. `TEST_ENV` has a default; the Atlassian pair is needed once the Jira host is set in `.agents/project.yaml`.\n- **tooling**: CI-only secrets (Slack, the private report portal). MCP servers that can run at harness level (web search, Postman) and CLI logins (`acli`, `resend`) are NOT `.env` keys at all: connect them once per machine and the skills resolve them by capability.\n- **project-under-test**: your app's login, database and API (`<ENV>_USER_*`, `DBHUB_*`, `API_BASE_URL`, `OPENAPI_SPEC_PATH`). Typed examples; rename or delete them when you adapt the framework.\n\nNothing blocks install, update or `setup:doctor`: a value is validated by the code that reads it, with a named error (ADR-0005).\n\n**The Atlassian site host is not one of them.** It lives in `.agents/project.yaml` -> `issue_tracker.atlassian_url` and is read with `bun run --silent jira:url` (`--slug` for the bare host `acli --site` wants). It was pulled out of `.env` because a stale copy inherited from the parent shell silently shadowed the file — `jira:sync-issues` rebuilt the local PBI cache from a dead Jira site and exited 0, and the Jira-Direct TMS provider would have written results there. A hostname is not a secret, and it is project identity, so it belongs in a versioned file that shows up in a diff.\n\n`.env.example` has the full template with per-var comments. Run `bun run setup:doctor` at any time to see which are still missing — it prints `pending_actions[].where` URLs for every credential.\n\n### When the installer tells you something is wrong\n\n| Stage                    | Check depth                                                                                                                     | Behavior                                                                                                                                                                                                  |\n| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Gate (`bun cli/preflight.ts --for setup --gate`, the first half of `package.json` `setup`) | Bun recent enough; the dependencies present. | Bun too old: stops with the upgrade command. `node_modules` missing: runs `bun install` itself.                                                                                    |\n| Preflight (`runSetupPreflight` in `cli/preflight.ts`) | Tools (Bun, Node, git, acli, engram, gh), at least one AI agent, the project folder, the download hosts, free disk, the `.env` permissions. Each row: ok, info, warning or block. | A block stops the run before anything is written, with the fix per row; the page offers \"check again\". acli or engram missing only warn. `bun cli/preflight.ts --for setup --json` prints the same checklist for CI and agents. |\n| Verification (`verifyExternalClis`) | PATH probe — runs `which <name>` (POSIX) or `where <name>` (Windows). Presence only, no version check.                          | A `found`/`missing` table; for missing entries a `quick:` install command (when cross-platform) + `docs:` URL. Non-blocking.                                                                      |\n| `bun run setup:doctor`   | Re-checks every MCP `.env` var the manifest declares + Playwright browser cache + plaintext MCP credential copies an older version left (`bun run harness:env` retires them), and, per harness in use (`harnesses:`), the MCP and hook surfaces. | Human-readable or `--json` report. Every `pending_action` carries a `where` hint or URL — re-run any time after partial setup.                                                                            |\n\n> **TL;DR**: install **Bun** plus at least one of **Claude Code, OpenCode, or Codex** before you run setup. Everything else, the installer points you at when you hit it.\n\n<br />\n<br />\n\n## Start here — pick your path\n\n| Goal                                                  | What to read / run                                                                                                                                                                                     |\n| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| **Start a new project — magic command (recommended)** | `bunx create-agentic-qa@latest <your-repo-name>` — official scaffolder ([npm](https://www.npmjs.com/package/create-agentic-qa))                                                                               |\n| **Start a new project — GitHub \"Use this template\"**  | Click [**Use this template**](https://github.com/upex-galaxy/agentic-qa-boilerplate/generate) → clone your new repo → `bun install && bun setup` (see [Other ways to start](#other-ways-to-start)) |\n| **Contribute to the boilerplate itself**              | `git clone …` then `bun install && bun setup` (see [Other ways to start](#other-ways-to-start))                                                                                                    |\n| **Get oriented before installing**                    | `bun run onboarding` — opens the docs site on its \"Empezar aquí\" page (`docs/core/empezar-aqui.html`)                                                                                                   |\n| **Understand the methodology**                        | `bun run docs` → Metodología ([IQL](docs/core/metodologia/iql.html), [how this repo implements it](docs/core/metodologia/este-repo.html)); official site: [upexgalaxy.com/metodologia](https://www.upexgalaxy.com/metodologia) |\n| **Browse the human docs**                             | `bun run docs` — local HTML site (setup guides, methodology, exploration); add your own pages under any `docs/` folder except `docs/core/`                                                                |\n| **See what `bun setup` configures**               | [`INSTALLER.md`](INSTALLER.md) — run `bun run setup:doctor` after setup                                                                                                                               |\n| **You're an AI agent**                                | [`AGENTS.md`](AGENTS.md) (auto-loaded each session on every supported harness; it routes to `.agents/instructions/`)                                                                                    |\n\n> First-timers, use the scaffolder. It handles tarball download, git scrub, rename, `bun install`, and the installer in one shot. The manual clone is for people hacking on the boilerplate itself.\n\n<br />\n\n## What this is\n\nA starter for QA teams that want AI agents driving the testing workflow — not isolated test snippets, but the whole loop. Plan a sprint, document test cases in Jira/Xray, write KATA-compliant Playwright tests, run regression, sign off the release. One workflow skill per stage covers the phases; the catalogue is `.agents/skills/REGISTRY.md`. Support skills handle the chores around them, each one selected by name plus mode. The development half (project foundation, sprint dev, deploys) lives in [agentic-dev-boilerplate](https://github.com/upex-galaxy/agentic-dev-boilerplate) — pair them or use one.\n\n<br />\n\n## Scaffold a new project\n\n`create-agentic-qa` is the official scaffolder ([npm](https://www.npmjs.com/package/create-agentic-qa), source in [`packages/create-agentic-qa/`](packages/create-agentic-qa/)). One command, full setup:\n\n```bash\nbunx create-agentic-qa@latest <your-repo-name>\ncd <your-repo-name>\n```\n\nWhat it does:\n\n1. Downloads `upex-galaxy/agentic-qa-boilerplate` (latest `main`) as a tarball — no git history.\n2. Rewrites `package.json` name + `.agents/project.yaml` `project.project_name` (and `project.project_key` when `--project-key` is passed).\n3. Initializes a fresh `git init -b main` with an initial commit.\n4. Runs `bun install`.\n5. Hands off to `bun setup`, which opens a local page in your browser (or stays in the terminal where there is no screen, see [`INSTALLER.md`](INSTALLER.md#where-the-run-is-shown)) — Engram memory, the committed skills under `.agents/skills/`, community skills, the MCP servers `.mcp.json` declares, where secrets live (`.env` by default, or a secret manager), `.env`, optional `gh repo create`. It records the agents you pick in `harnesses:` (`.agents/project.yaml`) and offers to delete the files of the harnesses you left out, and its last step retires plaintext MCP credential copies an older version left.\n\nUseful flags (full list in [`packages/create-agentic-qa/README.md`](packages/create-agentic-qa/README.md)):\n\n| Flag                           | Effect                                                          |\n| ------------------------------ | --------------------------------------------------------------- |\n| `--here`                       | Bootstrap into the current directory instead of a new one.      |\n| `--template <ref>`             | Pin to a branch / tag / SHA instead of `main`.                  |\n| `--template-repo <owner/repo>` | Use a fork instead of `upex-galaxy/agentic-qa-boilerplate`.     |\n| `--project-key UPEX`           | Pre-fill the Jira project key (otherwise prompted).             |\n| `--no-install` / `--no-setup`  | Skip `bun install` or the installer (`bun setup`).              |\n| `--non-interactive`            | Auto-pick defaults (also auto-detected when no TTY is present). |\n\nThen continue with the per-project workflow:\n\n```bash\n# Optional: open the docs site on its \"Empezar aquí\" page\nbun run onboarding\n\n# Optional, Claude Code only: configure the statusline in a SEPARATE terminal\nbunx -y ccstatusline@latest\n\n# Drive the QA lifecycle inside the agent:\n/agentic-qa-onboard     # first-time orientation tour\n/project-discovery      # reverse-engineer the target app into the domain + infra context maps\n/test-framework-adaptation        # wire KATA to the target stack (auth, vars, CI, MCP) — run once after discovery\n/shift-left-testing     # Shift-Left: pre-sprint AC refinement on backlog batch\n/sprint-testing         # in-sprint manual QA per ticket (plan + execute + report)\n/test-documentation     # TMS docs + ROI scoring (Candidate / Manual / Deferred)\n/test-automation        # KATA Plan -> Code -> Review\n/regression-testing     # CI execution + GO / CAUTION / NO-GO\n```\n\n> Don't chain `bun run onboarding && bun setup` — the docs server is blocking and the chain deadlocks. Run them as separate steps.\n\n> `bunx -y ccstatusline@latest` is Claude Code-only and optional. Run it from a plain terminal with NO agent running — concurrent TUIs fight over stdin and the configurator silently breaks. OpenCode users can add the `opencode-subagent-statusline` plugin to their own global config instead (OpenCode 1 only; see `INSTALLER.md`); the shared `opencode.jsonc` carries no cosmetic plugins.\n\n<br />\n\n## Launching the agent\n\n`.env` is the default source of credentials (a secret manager can hold them instead, ADR-0010), and no harness gets a copy of it. Every MCP server that needs `.env` values starts through the same `.env` loader in `.mcp.json`, `opencode.jsonc` and `.codex/config.toml` (`varlock run --no-redact-stdout --inject vars --filter <its vars> -- <server>`), so each server reads `.env` itself, and only its own variables, however the harness was opened. `bun run setup:doctor` reports any plaintext copy an older `bun run harness:env` left in `.claude/settings.local.json` or `.auth/opencode/`, and `bun run harness:env` retires it. After filling `.env`, restart the agent session (MCP servers read `.env` when the harness spawns them). From the repo root:\n\n```bash\nclaude        # Claude Code (or Claude Desktop)\nopencode      # OpenCode (or its desktop app)\ncodex         # Codex CLI (or Codex Desktop)\n```\n\nOpen the harness directly: no wrapper, no script. Starting it inside `varlock run` would export every `.env` value into the AI's own process, where any command it runs can read them (ADR-0014). Nothing in the repo exports `.env` into a shell: the test scripts (`bun run test`, `test:e2e`, ...) load it through `scripts/launch.ts`, which warns when a variable inherited from the shell differs from `.env.local` over `.env` (names and lengths only) and goes on, so a deliberate `AUTO_SYNC=true bun run test` still works. A project variable you export in your own shell profile still wins over `.env` for whatever starts from that shell, MCP servers included; `bun run vars:env:check` names it.\n\n**Codex Desktop** consumes the same repository configuration as the CLI — no second convention, no extra directory. Opened from Finder or the Dock it has no process environment, which is why every MCP server that needs `.env` values starts through the `.env` loader (the same one `.mcp.json` and `opencode.jsonc` use) instead of relying on `env_vars`: run `bun install` once (the loader is the `varlock` devDependency) and keep a filled `.env` at the project root. A worktree the Codex app creates gets `.env`, `.auth/` and the synced `api/openapi.json` from the committed `.worktreeinclude`. Two caveats apply to CLI and Desktop alike: Codex loads the project's `.codex/` config and hooks **only in a repository you have marked trusted** (`bun run setup:doctor` reports that trust on its own line, because it is runtime state no file check can verify), and a remote MCP you add at user level should authenticate with `codex mcp login` (OAuth), because `--bearer-token-env-var` reads the same process environment a Dock launch does not have.\n\nNothing needs `.env` exported into your shell, and no secret is: each process loads its own config. The Bun scripts (`bun run jira:*`, `bun run api:login`, `bun xray`) read `.env` through Bun's own autoload, `acli` uses its stored login (`acli jira auth login`) and `gh` its keyring.\n\n<br />\n\n<details>\n<summary><b>Other ways to start</b> — GitHub template flow + manual clone for contributors</summary>\n\n<br />\n\n### Use this template (GitHub)\n\nPrefer to start your project **on GitHub from day one** (your own repo, your own remote, full history under your account)? Use GitHub's native template flow:\n\n1. Click [**Use this template → Create a new repository**](https://github.com/upex-galaxy/agentic-qa-boilerplate/generate) on the boilerplate's GitHub page.\n2. Pick owner + name for your new repo, choose visibility, create.\n3. Clone YOUR new repo locally:\n\n   ```bash\n   git clone https://github.com/<your-org>/<your-repo>.git\n   cd <your-repo>\n   ```\n\n4. Install + configure:\n\n   ```bash\n   bun install\n   bun setup        # Engram, skills, community skills, .env wiring, MCPs\n   ```\n\n   The template copies the boilerplate's own filled `.agents/project.yaml` (its `MAINTAINER COPY:` header line says so). The project-metadata step (`bun run agents:setup`) detects it and offers to replace it with the blank template before asking anything; accept. Without a TTY, pass the consent explicitly: `bun run agents:setup --non-interactive --reseed`.\n\n5. (Optional) Rename the project inside the codebase: edit `package.json` → `name`, and `.agents/project.yaml` → `project.project_name`.\n\n> **The magic command does this better.** `bunx create-agentic-qa@latest <your-repo-name>` does everything the template flow does **plus**: scrubs the upstream git history (so your repo doesn't carry boilerplate commits), auto-rewrites `package.json` name and `.agents/project.yaml` `project.project_name`, runs `bun install`, runs the installer, and optionally creates the GitHub repo for you via `gh` — all in one command. The template route is a good fit only if you want the GitHub repo created via the web UI before any local work.\n\n### Manual clone (contributors)\n\nHacking on the boilerplate **itself** (skills, installer, scripts, docs)? Clone the repo directly:\n\n```bash\n# 1. Clone the repository\ngit clone https://github.com/upex-galaxy/agentic-qa-boilerplate.git\ncd agentic-qa-boilerplate\n\n# 2. Install dependencies\nbun install\n\n# 3. Install Playwright browsers\nbun run pw:install\n\n# 4. Copy env template\ncp .env.example .env   # fill in the values, then restart the agent session\n\n# 5. (Optional) Visual orientation — close tab + Ctrl-C when done.\nbun run onboarding\n\n# 6. Run the interactive setup (Engram, skills, MCPs, .env)\nbun setup\n\n# 7. Validate the install\nbun run setup:doctor\n```\n\n> End-users building a new project should NOT clone manually — use `bunx create-agentic-qa@latest` so git history is scrubbed and the project is renamed automatically.\n\n</details>\n\n<br />\n\n## How it works\n\nSkills auto-trigger when your prompt matches their `description` frontmatter — or you force-load with a slash command (`/sprint-testing`). Each skill is a `SKILL.md` plus a `references/` folder. The agent only reads what the current step needs, so context stays lean.\n\nProject values (URLs, project key, Jira fields) live in `.agents/project.yaml` and get injected into prompts through the variable syntaxes `.agents/README.md` defines. Skills are grouped by phase: onboarding (one-time discovery), in-sprint QA (continuous), automation (per story), regression (per release). The dev companion repo follows the same pattern.\n\n<br />\n\n## Features\n\n| Feature                    | Description                                                                        |\n| -------------------------- | ---------------------------------------------------------------------------------- |\n| **KATA Architecture**      | Komponent Action Test Architecture for clean test organization                     |\n| **Playwright**             | Modern browser automation with auto-waiting and tracing                            |\n| **Allure Reports**         | Rich test reports with history and trends                                          |\n| **TMS Sync**               | Test results pushed to Jira/Xray by `bun run test:sync` after the run (off by default: `AUTO_SYNC`) |\n| **Context Engineering**    | Context skills hold the AI's map of your app (`bun run context:map`); `.context/` is a regenerable cache of Jira and the API spec |\n| **Skills-based Workflows** | Agent skills under `.agents/skills/` drive the AI-assisted QA and automation flows |\n| **Multi-harness**          | One instruction body and one skill store, consumed by Claude Code, OpenCode and Codex; a project keeps only the harnesses it declares in `harnesses:` (`.agents/project.yaml`) |\n| **Secrets by name**        | `.env` (or a secret manager, ADR-0010) is read per process; MCP servers read it through a filtered `.env` loader (ADR-0011); the AI never sees a value (Critical Rule #1) |\n| **MCP Integration**        | Local servers declared once in `.mcp.json` and mirrored for each host; browser automation runs through `/playwright-cli`, with no browser MCP |\n\n<br />\n\n## Configuration\n\nThis boilerplate has **two configuration systems** that serve different consumers and must not be conflated:\n\n| System                     | File                           | Consumer                                                                                                                | Loaded at            |\n| -------------------------- | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------- | -------------------- |\n| **Runtime test config**    | `.env` + `config/variables.ts` | Playwright runner, KATA components, `bun run *` scripts (jiraSync, env validate, etc.)                                  | Test execution time  |\n| **AI context engineering** | `.agents/project.yaml`         | Every supported harness (Claude Code, OpenCode, Codex) — used to resolve `{{VAR}}` references in skills, commands, and templates | AI session bootstrap |\n\nBoth are needed. Skip neither.\n\n### (a) Runtime test config — `.env`\n\nCopy `.env.example` to `.env` and fill in what your project uses. Secrets are typed by you (or live in the secret manager); the AI writes a non-sensitive value only through `bun run env:set KEY=value`, which refuses any `@sensitive` or secret-looking key. `.env.example` is the annotated template, one comment per variable and the defaults of the optional ones as commented lines; `cli/lib/variables-manifest.ts` declares each known variable's scope (framework, tooling, project-under-test) and the feature switch that makes it required. `bun run setup:doctor` reports which ones are still missing.\n\nEvery variable also has a **typed schema**, read by [varlock](https://varlock.dev): `.env.core.schema` (generated from `cli/lib/variables-manifest.ts`, synced by `bun sync`) plus `.env.schema` (yours: root decorators and the variables your project-under-test adds). Neither holds a value. Validate your `.env` against it with sensitive values redacted:\n\n```bash\nbunx varlock load --agent      # exit 0 = every required item for your TEST_ENV is set\nbunx varlock explain TEST_ENV  # where one value comes from\nbun run vars:schema            # regenerate .env.core.schema after editing the manifest\n```\n\nThe schema is a gate (pre-commit freshness, pre-push warn-only, first step of `build.yml`; the wiring is in `.husky/framework-gates.sh` and `.github/workflows/build.yml`), and `vars:schema:check` fails any secret-looking key (TOKEN, SECRET, PASSWORD, API_KEY, ...) not marked `@sensitive`, naming it with its `file:line`. The `test*` scripts start through `varlock run` (`scripts/launch.ts`); other Bun scripts read `.env` through Bun's autoload. The standalone `varlock` binary is optional: the pinned devDependency covers every gate. Install it with `brew install dmno-dev/tap/varlock` (macOS), `curl -sSfL https://varlock.dev/install.sh | sh -s` (Linux) or `npm i -g varlock` (Windows PowerShell / cmd; documented by varlock, not measured here).\n\nQuote any value that contains a `#` (`PASSWORD=\"pass#word\"`): varlock cuts an unquoted value at the first `#`.\n\n**Secret manager (advanced, optional).** `.env` stays the default. A team that keeps its secrets in a vault picks 1Password in `bun setup`, which records `secrets:` in `.agents/project.yaml` and writes `.env.provider.schema`: committed `op://` references, never values, imported by `.env.core.schema` only when present. Desktop-app auth on laptops, a service account in CI; a non-empty `.env` value still wins. Bun's own `.env` autoload does not resolve the vault references, so a script that needs a vault secret runs through `bunx varlock run -- <cmd>`. Steps: `INSTALLER.md` (\"Secret manager (advanced)\"); decision: `.context/ADR/ADR-0010-secret-manager-advanced-option.md`.\n\n### (b) Runtime URLs — `config/variables.ts`\n\nUpdate `envDataMap` in `config/variables.ts` with your application URLs. The `Environment` type in `config/variables.ts` lists the accepted values; extend it there when you need another environment. The shape (two environments shown):\n\n```typescript\nconst envDataMap: Record<\n  Environment,\n  { base: string; api: string; user: { email: string; password: string } }\n> = {\n  local: {\n    base: 'http://localhost:3000',\n    api: 'http://localhost:3000/api',\n    user: userCredentialsMap.local,\n  },\n  staging: {\n    base: 'https://staging.yourapp.com',\n    api: 'https://staging.yourapp.com/api',\n    user: userCredentialsMap.staging,\n  },\n};\n```\n\n### (c) AI context engineering — `.agents/project.yaml`\n\nEvery supported harness (Claude Code, OpenCode, Codex) resolves `{{VAR}}` references in skills, templates, and commands against `.agents/project.yaml`. Edit it manually, or run the interactive walkthrough:\n\n```bash\nbun run agents:setup\n```\n\nThis populates project identity (`project.project_name`, `project.project_key`), `issue_tracker.atlassian_url` and the per-environment leaves under `environments.<env>`; the full key set is `.agents/project.schema.yaml`. `bun setup` also writes `harnesses:` (the harnesses the gates check) and `secrets:` (where secret values live). See `.agents/README.md` for the convention.\n\n<br />\n\n## Run Tests\n\n```bash\n# Run all tests\nbun run test\n\n# Run with UI mode (recommended for development)\nbun run test:ui\n\n# Run specific test types\nbun run test:e2e           # E2E tests only\nbun run test:integration   # API tests only\nbun run test:smoke         # smoke / @critical tests\n```\n\n<br />\n\n## Project Structure\n\n```\n├── tests/\n│   ├── components/               # KATA Components Layer\n│   │   ├── TestContext.ts        # Layer 1: Base utilities + faker\n│   │   ├── TestFixture.ts        # Layer 4: Unified test fixture\n│   │   ├── api/                  # API components\n│   │   │   ├── ApiBase.ts        # Layer 2: HTTP client base\n│   │   │   └── ExampleApi.ts     # Layer 3: Domain component\n│   │   ├── ui/                   # UI components\n│   │   │   ├── UiBase.ts         # Layer 2: Page base\n│   │   │   └── ExamplePage.ts    # Layer 3: Domain component\n│   │   └── steps/                # Reusable ATC chains (preconditions)\n│   │\n│   ├── e2e/                      # E2E test specs\n│   │   └── module-example/       # Example module\n│   ├── integration/              # API integration tests\n│   │   └── module-example/       # Example module\n│   ├── setup/                    # Test setup files\n│   │   ├── global.setup.ts       # Global setup\n│   │   └── ui-auth.setup.ts      # UI authentication\n│   ├── data/\n│   │   ├── fixtures/             # Static test data (JSON)\n│   │   ├── types.ts              # Test data types\n│   │   └── DataFactory.ts        # Dynamic data generation\n│   ├── utils/                    # Test utilities\n│   │   ├── decorators.ts         # @atc decorator\n│   │   └── jiraSync.ts           # TMS synchronization\n│   └── KataReporter.ts           # Terminal reporter\n│\n├── config/\n│   ├── variables.ts              # Runtime env vars consumed by Playwright/KATA\n│   └── validateTestEnv.ts        # Test environment validation\n│\n├── .context/                     # Script caches + the few files this repo owns (ignored by default; see .gitignore)\n│   ├── ADR/                      # Test-architecture decision records (committed, append-only)\n│   ├── project-config.md         # Project config written by /project-discovery (committed)\n│   ├── reports/                  # Generated output (GITIGNORED except its README): test map, regression reports\n│   └── PBI/                      # Per-ticket backlog items + the MTP cache (GITIGNORED Jira cache; `bun run context:hydrate`)\n│\n├── .agents/                      # Agentskills.io spec layout — the shared, harness-agnostic substrate\n│   ├── project.yaml              # AI context vars (resolved as {{VAR}} by skills)\n│   ├── jira-fields.json          # Jira custom-field catalog (synced by `bun run jira:sync-fields`)\n│   ├── jira-required.yaml        # Required Jira custom-field manifest\n│   ├── README.md                 # Variable conventions reference\n│   ├── hooks/                    # personality-reinject.mjs (one emitter, three harness adapters) + doc-contracts.mjs (edit-time DOCS: line)\n│   ├── instructions/             # AGENTS.md sections, loaded on demand by the ROUTER (agent-project.md = this project's own)\n│   └── skills/                   # THE skill store — read by all three harnesses\n│       └── <skill>/              # one folder per skill; the catalogue is REGISTRY.md (generated)\n│\n├── .claude/                      # Claude Code adapter — settings.json (hook) + generated skills alias\n├── .opencode/                    # OpenCode adapter — plugins/personality-reinject.js\n├── .codex/                       # Codex adapter — config.toml (MCP) + hooks.json. Shared by CLI and Desktop\n│\n├── .github/workflows/            # CI/CD pipelines, one file per workflow (see CI/CD Pipelines below)\n│\n├── docs/                         # Human docs site (`bun run docs`)\n│   ├── index.html                # Portal (sidebar built from each page's meta)\n│   ├── assets/                   # Shared docs.css / docs.js, IQL diagrams\n│   └── core/                     # Shipped pages (synced); other folders are project-owned\n│\n├── packages/                     # Boilerplate-only (pruned from scaffolds): the npm scaffolder, the published decks site\n│\n├── cli/                          # install.ts, doctor.ts, update-boilerplate.ts consumed by bun scripts\n│\n├── playwright.config.ts          # Playwright configuration\n├── INSTALLER.md                  # Contract for bun setup — what each installer layer does\n├── AGENTS.md                     # AI memory, always-on layer: binding rules, behaviour and the ROUTER, loaded by all three harnesses\n├── CLAUDE.md                     # One-line shim (`@AGENTS.md`) so Claude Code reaches it. Never holds prose\n├── .mcp.json                     # MCP config — Claude Code\n├── opencode.jsonc                # MCP config — OpenCode\n└── package.json                  # Scripts and dependencies\n```\n\n<br />\n\n## KATA Architecture\n\nThis boilerplate implements **KATA** (Komponent Action Test Architecture).\n\n### Architecture Layers\n\n```\nTestContext (Layer 1)\n    ↓ extends\nUiBase / ApiBase (Layer 2) ← Helpers here\n    ↓ extends\nYourPage / YourApi (Layer 3) ← ATCs here\n    ↓ used by\nTestFixture (Layer 4) ← DI entry point\n    ↓ used by\nTest Files ← Orchestrate ATCs\n```\n\n### Component Types\n\n| Component | Purpose             | Location                  |\n| --------- | ------------------- | ------------------------- |\n| **Api**   | HTTP interactions   | `tests/components/api/`   |\n| **Page**  | UI interactions     | `tests/components/ui/`    |\n| **Step**  | Reusable ATC chains | `tests/components/steps/` |\n\n### Example Test\n\nThe ATC lives in a component and carries the Jira key; the spec only orchestrates it through a fixture (`{ ui }`, `{ api }` or `{ test }`):\n\n```typescript\n// tests/components/ui/LoginPage.ts (the ATC: one complete mini-flow)\n@atc('UPEX-101')\nasync loginSuccessfully(credentials: LoginCredentials): Promise<void> {\n  await this.fillAndSubmitLoginForm(credentials);\n  await expect(this.page).not.toHaveURL(/.*\\/login.*/);\n}\n\n// tests/e2e/auth/login.test.ts (the spec)\nimport { test } from '@TestFixture';\nimport { config } from '@variables';\n\ntest.describe('UPEX-100: Login', () => {\n  test('UPEX-101: should log in with valid credentials', async ({ ui }) => {\n    await ui.login.goto();\n    await ui.login.loginSuccessfully({\n      email: config.testUser.email,\n      password: config.testUser.password,\n    });\n  });\n});\n```\n\nSee the `/test-automation` skill (`references/kata-architecture.md`) for complete documentation.\n\n<br />\n\n## Available Scripts\n\nThe scripts live in `package.json` (Critical Rule #11: read it there, never a copy), grouped by prefix. `bun run docs` opens the human guide, whose \"Empezar aquí\" page walks the same groups.\n\n### Test Execution\n\nThe `test*` family in `package.json`: the whole suite, UI mode, the subsets by type or tag, retries and last-failed re-runs.\n\n### Reports\n\nThe Playwright report, the `allure:*` family and the TMS sync (`test:sync`), all in `package.json`.\n\n### Code Quality\n\nLint, format and type checks plus `repo:check`, whose chain in `package.json` is the set of gates the pre-push hook approximates (`repo:fix` auto-fixes format and lint first).\n\n### Utilities\n\nBrowser install, test-environment validation and artifact cleanup: the small scripts near the top of `package.json`.\n\n### CLI Tools\n\nThe updater, Xray CLI, OpenAPI sync, KATA manifest, `.agents/` setup and linting, the instruction gates (`instructions:*`), the `.env` writer for non-sensitive values (`env:set`), the retirement of old plaintext MCP credential copies (`harness:env`), worktree provisioning and audit (`worktree:*`), git-policy parity, the Jira catalog syncs and the PBI cache: one script per tool in `package.json` (the full list), each with its own usage text.\n\n> **`--upex` flag** — the catalog sync scripts (`jira:sync-fields`, `jira:sync-workflows`, `jira:sync-link-types`) accept `--upex` to download the UPEX-standard reference JSON from `upex-galaxy/agentic-qa-boilerplate@main` instead of hitting Jira. Use when you don't have admin access, when you want a working catalog without setting up auth, or when you want the canonical UPEX standard as a reference. Examples: `bun run jira:sync-fields --upex`, `bun run jira:sync-workflows --upex`, `bun run jira:sync-link-types --upex`. `jira:sync-issues` does not take the flag — it always pulls from your Jira instance.\n\n<br />\n\n## Keeping your project in sync with the boilerplate\n\n`bun sync` keeps your project aligned with the official template by tracking which upstream commit each piece of the framework (`.agents/skills/`, `scripts/`, `cli/`, `.husky/`, ...) was last synced from. Instead of overwriting framework files blindly, it:\n\n1. Reads `.template/boilerplate.lock.json` (committed in your repo) to find the last-synced SHA per component\n2. Clones the template lazily (sparse checkout, only the dirs that get synced)\n3. Computes the exact list of changed files between your synced SHA and template HEAD\n4. Classifies each file: clean fast-forward, locally diverged, new upstream, deleted upstream, binary, or whitespace-only\n5. Applies the plan for the mode you chose (see the flags below). Every overwrite is backed up (`.backups/`, restorable with `--rollback`) and the exact diff stays visible in git history\n6. Regenerates the derived surfaces from their sources (`bun run agents:compat`, `skills:registry`, `kata:manifest` logic) and runs the project's own gates\n7. Closes with one \"Estado por superficie\" table and ONE parity prompt for your AI (details below)\n\nBefore step 1 a preflight checks, in seconds, what would make the update fail halfway (Bun, git, the `gh` session, `github.com`, a primary checkout, the lock file, disk) and stops before anything is written when something blocks. On a terminal with a screen the run is shown in a local page: every question (areas, files with their diff, files changed on both sides, deletions, new `package.json` keys) is answered there BEFORE the first file is written, and the closing report shows the same table; the terminal keeps the whole log. `--headless` keeps the run in the terminal, `--ui` forces the page, and CI, a pipe, an agent shell or SSH without a display never open it ([`INSTALLER.md`](INSTALLER.md#how-a-bun-sync-run-goes)). A synced file you edited that upstream also changed now asks (decide later, keep mine, take the new one) instead of being overwritten.\n\n**Requirements**: git 2.25 or newer (partial clone with `--filter=blob:none`), Bun, GitHub CLI authenticated (or `UPEX_TEMPLATE_REPO` pointing at a local clone).\n\n| Flag | Effect |\n| ---- | ------ |\n| (none) | Interactive flow, in the page or the terminal: pick areas, review files, resolve divergences, confirm deletions |\n| `--auto` | Non-interactive: copies new files and overwrites divergences with upstream (backup in `.backups/`). Never deletes files upstream removed. Typed on a real terminal, it shows a read-only page |\n| `--force` | Like `--auto`, and also deletes files upstream removed (backup + `--rollback` still apply) |\n| `--interactive`, `-i` | Keeps the prompts even when stdin is not a terminal |\n| `--dry-run` | Preview without writing. Prints the parity table; the prompt is not saved. With a newer updater upstream, the preview runs the NEW updater from the upstream clone, so it shows what the real run will do |\n| `--strict` | Exit 1 when the run ends with a BLOCKING parity finding (compat contract broken: alias, a command shadowing a skill, hooks, MCP). Default: warn, exit 0. Drift on protected files never blocks |\n| `--no-gates` | Skip the post-sync gates |\n| `--headless` / `--ui` | Keep the run in the terminal / open the page even where the rules say terminal |\n| `--rollback` | Restore the most recent backup |\n| `--skill a,b` / `--list` | Sync only the named skills / list the skills the template offers |\n\nWithout a TTY on stdin and no `--auto` / `--interactive`, the run assumes `--auto` and says so in one line instead of waiting on the phase-3 multi-select. `UPEX_TEMPLATE_REPO` points the updater at a fork (`OWNER/REPO`) or at a local clone (absolute path or `file://`), which is how an unpublished branch is tested against a consumer.\n\n**What a run leaves behind.** Every run ends with a single \"Estado por superficie\" table (one row per surface, one ok or warn glyph per row) followed by ONE parity prompt, also saved to `.agents/prompts/parity-plan.md` (gitignored, single-use). The prompt lists every difference between the project and upstream as a numbered row with concrete evidence (headings added or removed in `AGENTS.md`, hunk counts, server ids missing from a host, archived skill collisions) and asks the AI to present the table and WAIT for a per-row decision, `keep project | take upstream | merge`, before editing anything. One row per path: a watched file that also fails a compat contract (say `.codex/config.toml` missing a server) is one blocking row carrying both pieces of evidence. `take upstream` is suggested only where the project lacks the content entirely; a row naming project-only servers, keys, headings or edits says `merge`, and every `merge` on a watched file says what to port and what to keep (`port upstream additions only: <keys>; keep project-only: <keys>`). Rows on `package.json` (a key kept at the project's value, both values in the saved file) and on `Verificación` (a gate that failed: exit code, first error lines, which applied files it names) are informational, never blocking.\n\n**Protected files and `updater.protected_paths`.** The protected watchlist (`PROTECTED_WATCHLIST` in `cli/update-boilerplate.ts`: `AGENTS.md`, `.mcp.json` and the rest) is never overwritten (also under `--auto` and `--force`): they only appear in the parity report, one drift row per upstream change. `.claude/settings.json`, `.codex/` and the husky hooks are delivered once when missing (bootstrap-only); after that the `permissions.allow` and `permissions.deny` lists and the `hooks` of `.claude/settings.json` only grow (upstream entries the project lacks are appended, a missing hook command as a new group after the project's own; a deny the project does not want is declined in `updater.declined_denies`, a hook command in `updater.declined_hooks`), so a hook group `agents:compat:check` starts requiring arrives with the same `bun sync`, and `opencode.jsonc` gets a paste row for the deny rules it lacks instead of a write. The files of a harness the project left out of `harnesses:` are never delivered, and a leftover `.envrc` gets one informational row saying it can be deleted. A project protects any other synced file it merged by hand through `updater.protected_paths` in `.agents/project.yaml` (repo-relative file paths, same semantics; a path outside the repo, under `.git`, a directory or a non-string is reported and ignored). The row for an overwritten project edit names its `.backups/` copy and ends with that fix; the saved prompt repeats it as the YAML to paste. `.agents/project.yaml` and `.agents/jira-required.yaml` are compared by structure only: an `informational` row for keys upstream added, no row for value differences (project identity).\n\n**Safe re-runs and aborts.** The sync leaves its files uncommitted on purpose (review the prompt first). The run records what it wrote in `.template/last-apply.json` (gitignored, hashed), and the dirty-tree guard recognises those paths while their hash still matches, so `bun sync` twice in a row without committing is a no-op instead of an abort. A synced path edited by hand since, or an unrelated dirty synced path, still aborts, naming `Commit sugerido` and the prompt path. Uncommitted changes outside the paths the updater writes (your tests, your code, protected files) never block. A run that applies nothing leaves the tree byte-identical (the lock is not rewritten). An aborted run (dirty tree, corrupt lock, failed clone, declined migration or self-update) prints `Abortado.` and exits 1, never a success line.\n\n**Generated surfaces.** `CLAUDE.md` (the `@AGENTS.md` shim), `.claude/skills` (the alias), `.agents/skills/REGISTRY.md` and `kata-manifest.json` are rebuilt after every sync and never reported as drift. On the run that migrates a Claude-era project, the `.claude/skills` alias is deliberately NOT created (git cannot rewrite the staged `.claude/skills/*` deletions behind a symlink, so the pre-commit hook would fail): commit the migration, then `bun run agents:compat` creates it; the closing box says so, and any re-run before that commit keeps deferring it.\n\n**Dirty-tree guard, cursors and MCP rows.** The dirty-tree guard blocks only on uncommitted work the sync would overwrite (a synced component file, an ignore file, `package.json`); dirt anywhere else (`tests/`, KATA code, a protected file) is listed as `N ruta(s) con cambios sin commitear fuera de lo que este updater escribe; no bloquean` and never aborts `--auto`. A path upstream added after the lock cursor never gets a \"project edit overwritten\" row. A repo that still tracks `.context/PBI/` in git gets ONE Componentes row (`N tracked path(s) still in git ...; migration recipe saved to .agents/prompts/pbi-cache-migration.md`) with the full recipe in that file, never a terminal dump. A path just declared in `updater.protected_paths` gets its marker seeded with no row; its drift row fires on the next upstream change. The `cli` lock cursor advances after a self-update, with or without the env signal (an older parent is caught by content). MCP registry rows compare each server whole and say what differs (`context7: args differ`, `supabase: env keys differ`), naming the first few servers and counting the rest.\n\n**Polish behaviours.** A heading changed only by punctuation (em dash, en dash, hyphen, colon) counts as unchanged; the skills registry regenerates after the parity report (the KATA manifest hook keeps its own place ahead of the gates), and an overwritten `.agents/skills/` row says to rerun it once restored; a watched file with no marker yet whose upstream copy has not changed since the lock cursor seeds silently instead of firing a row; and the closing box names why gates did not run, `Gates: omitidas (sin cambios)` or `omitidas (--no-gates)`, instead of dropping the line. The release history behind these behaviours is recorded in `.context/ADR/ADR-0006-forensic-measurements-ledger.md`.\n\n```bash\nbun sync                # interactive\nbun sync --auto         # unattended, no deletions\nbun sync --dry-run      # preview (parity table included)\nbun sync --strict       # CI: exit 1 on a blocking parity finding\nbun sync --rollback     # restore the latest backup\n```\n\nThe `.template/boilerplate.lock.json` file is committable: commit it so your team and CI know exactly which template version each component is on.\n\n<br />\n\n## CI/CD Pipelines\n\n### GitHub Actions Workflows\n\nThe workflows are the files in `.github/workflows/`; each one's `on:` block is its trigger (a PR to `main`, a cron, a manual dispatch). The suite workflows (smoke, sanity, regression) run the Playwright tags and patterns their names say, `build.yml` validates that the framework compiles, and the `pages*` workflows publish and maintain the decks site.\n\n### Environment Secrets\n\nThe framework requires nothing but `TEST_ENV` (it has a default). The test-user pair is a project-under-test example: the suite workflows inject it as secrets, and `config.testUser` fails with a named error the first time a test reads an empty pair. Nothing validates it up front, so a fork PR with no secrets still builds.\n\nEvery other variable is optional and switched on by a feature (browser tuning, the TMS sync, Xray, the Atlassian pair, reporting). `.env.example` is the annotated list, with the default of each optional one as a commented line; `cli/lib/variables-manifest.ts` declares the scope and feature switch of every variable the installer, `setup:doctor` and the updater know; each suite workflow's `env:` block shows which ones CI passes. The Atlassian site host is never one of them: it is `issue_tracker.atlassian_url` in `.agents/project.yaml`.\n\nA project on the secret manager (ADR-0010) adds one more secret, `OP_SERVICE_ACCOUNT_TOKEN`, which the suite workflows already pass. It sits beside the per-variable secrets, never instead: a per-variable secret with a value still wins, and an unset one no longer blanks the vault value (the test launcher, `scripts/launch.ts`, drops the empty copy). Steps that call a tool directly (`bun xray`, the portal publisher) read only the per-variable secrets.\n\n`TMS_PROVIDER` is the one of them that is **not** a secret. `.github/workflows/regression.yml` reads it as `${{ vars.TMS_PROVIDER || 'xray' }}`, so set it as a repository **Variable** (Settings → Secrets and variables → Actions → Variables), not as a secret. A job-level `if:` can read `vars` but never `secrets`, and the Xray import job gates on exactly that expression — stored as a secret it is invisible to the gate and the step stays on the default forever.\n\n<br />\n\n## Skills\n\n### Workflow skills (auto-trigger)\n\nThe catalogue is `.agents/skills/REGISTRY.md` (generated by `bun run skills:registry`: one row per committed skill with its trigger and purpose); `.agents/instructions/agent-skills-and-mcps.md` carries the same table for the agent. What stays stable is the stage ownership:\n\n| Stage | Owner skill |\n| ----- | ----------- |\n| Shift-Left, pre-sprint on a backlog batch | `/shift-left-testing` |\n| Planning, Execution, Reporting: in-sprint manual QA | `/sprint-testing` |\n| Documentation: TMS test cases + ROI scoring | `/test-documentation` |\n| Automation: Plan → Code → Review on KATA | `/test-automation` |\n| Regression: regression / smoke / sanity and the GO / CAUTION / NO-GO verdict | `/regression-testing` |\n\nEverything else (onboarding, discovery, framework adaptation, git, Jira administration, orchestration, review, the CLI wrappers) is a support skill: its row in the registry says when it loads.\n\n### Reusable community skills (installed by `bun setup`)\n\nThese aren't committed in this repo. The installer fetches them via `bunx skills add` from upstream community repositories. The exact list lives in `cli/install.ts` — source of truth, changes faster than this README, consult the file directly.\n\n### Skill tiers (T1–T4)\n\nEvery skill belongs to one of four tiers. Each tier has different discovery and load rules. Full contract: [`.agents/skills/agentic-qa-core/references/skill-composition-strategy.md`](.agents/skills/agentic-qa-core/references/skill-composition-strategy.md).\n\n| Tier   | What                                   | Location                                              | Load behavior                                               |\n| ------ | -------------------------------------- | ----------------------------------------------------- | ----------------------------------------------------------- |\n| T1     | Project-owned (this repo)              | `.agents/skills/`                                     | Silent — load on trigger                                    |\n| T2     | Vendored (upstream, attribution kept)  | `.agents/skills/judgment-day/`                        | Silent on explicit trigger or host orchestrator citation    |\n| T2-opt | Optional SDD bundle (user-installed)   | `~/.claude/skills/sdd-*` only if present on the machine | Silent inside `/framework-development` only — see anti-leak |\n| T3     | Community project-level                | Installed by `install.ts` `PROJECT_LEVEL_SKILLS`      | Silent if matched by category                               |\n| T4     | Community user-level (global)          | Installed by `install.ts` `USER_LEVEL_SKILLS`         | Silent when a doctrine or your standing instructions name it for the moment; otherwise **ASK** before load |\n\nT3 project-level community skills install into the same `.agents/skills/` store, so there is never a second copy per harness. T4 user-level skills stay harness-specific (`~/.claude/skills/`, and the equivalent for each host). Which doctrine loads each one is the \"Loaded by / when\" table in `.agents/skills/agentic-qa-onboard/SKILL.md`, and `bun run skills:check` fails when an installed user-level skill has no row there.\n\nValidation: `bun run skills:check` checks tier coherence (orphan categories, tier mismatches, missing sections, stale doc paths).\n\n### Invoking a skill mode\n\nThere are no command files on any harness. A skill is invoked by its own name plus a mode: when the first token of `$ARGUMENTS` matches one of the skill's modes, that token is the mode and the rest is forwarded to it; with no match, the skill asks. On Claude Code that is `/<skill> <mode>` through `.claude/skills`, for example `/project-context data`. On OpenCode and Codex, name the skill and the mode in prose (\"load `project-context`, mode `data`\").\n\nThe modes of each skill are in its `## Mode routing` section; the skills themselves are listed in `.agents/skills/REGISTRY.md`.\n\n<br />\n\n## Variables system\n\nThe `.agents/` directory hosts the variable system every skill and command uses. The main forms:\n\n| Syntax                         | Purpose                                      | Resolves from                                             |\n| ------------------------------ | -------------------------------------------- | --------------------------------------------------------- |\n| `{{VAR_NAME}}`                 | Static project value (flat or env-scoped)    | `.agents/project.yaml`                                    |\n| `{{environments.<env>.<var>}}` | Explicit cross-env reference                 | `.agents/project.yaml` -> `environments.<env>.<var>`      |\n| `<<VAR_NAME>>`                 | Session/runtime value (e.g. `<<ISSUE_KEY>>`) | Computed by the calling prompt at runtime                 |\n| `{{jira.<slug>}}`              | Jira custom field reference                  | `.agents/jira-required.yaml` + `.agents/jira-fields.json` |\n\nSee `.agents/README.md` for the full contract.\n\n**Validation scripts:**\n\n```bash\nbun run vars:check         # Every {{VAR}} and {{jira.*}} reference resolves\nbun run jira:sync-fields   # Discover Jira custom fields -> .agents/jira-fields.json\nbun run jira:check         # Validate jira-required.yaml against jira-fields.json\n```\n\n<br />\n\n## TMS Integration (Jira/Xray)\n\nTwo TMS modalities are supported out of the box:\n\n- **Modality jira-xray**: full Xray entities (Test, Test Plan, Test Execution, Test Run, Pre-Condition). Primary tooling is the `/xray-cli` skill plus `/acli` for generic Jira issues.\n- **Modality jira-native (no Xray)**: ATP/ATR live as Story custom fields + comment mirrors; TCs live as Jira `Test` issues. All TMS operations fall through to `/acli`. See `.agents/skills/test-documentation/references/jira-setup.md`.\n\nFor how skills resolve `[ISSUE_TRACKER_TOOL]` and `[TMS_TOOL]` tags to concrete CLIs or MCPs, see `.agents/instructions/agent-tool-resolution.md` Tool Resolution.\n\n### Configuration\n\n1. Get Xray API credentials from Jira\n2. Add to `.env`:\n\n```bash\nXRAY_CLIENT_ID=your-client-id\nXRAY_CLIENT_SECRET=your-client-secret\nXRAY_PROJECT_KEY=YOUR-PROJECT\nAUTO_SYNC=true\n\n# Key of the Test Execution this run imports into: the RTR by default (created per\n# regression run by /regression-testing, linked to the RTP), the sprint-close STR at\n# sprint close; never a Test Plan key. Both live under the \"QA Test Artifacts\" epic.\n# Leave it empty and every run mints a brand-new, unparented Test Execution instead.\nSTP_EXECUTION_KEY=YOUR-PROJECT-194\n# Optional, local-only: the RTP key, so an Execution minted by the local fallback is\n# at least linked to the plan. No workflow reads it.\nRTP_KEY=YOUR-PROJECT-60\n```\n\n### Sync Test Results\n\n```bash\n# Always a separate step, AFTER the Playwright process exits: reports/atc_results.json\n# is written by KataReporter.onEnd(), so nothing inside the run can read it.\nAUTO_SYNC=true bun run test\nbun run test:sync\n```\n\nIn CI the suite workflows do exactly that: a `Sync Results to TMS` step gated on\n`AUTO_SYNC == 'true'` runs right after the test step. The global teardown only\nprints the ATC coverage summary; it never syncs.\n\n### Link Tests to Test Cases\n\n```typescript\n// The @atc decorator goes on the ATC method, never in the test title\n@atc('UPEX-101')\nasync loginSuccessfully(credentials: LoginCredentials): Promise<void> {\n  // ...\n}\n```\n\n`KataReporter` collects each ATC's result under its Jira key into `reports/atc_results.json`, which `bun run test:sync` pushes to the TMS.\n\n<br />\n\n## Customization Guide\n\n### 1. Update Project Identity\n\nEdit these files:\n\n- `package.json` — name, description, repository\n- `AGENTS.md` — the canonical AI memory's always-on layer, loaded by Claude Code, OpenCode and Codex alike; detail lives in `.agents/instructions/`, and this project's own rules go in `.agents/instructions/agent-project.md`. Never edit `CLAUDE.md`: it is the generated one-line shim that points here\n- `.agents/project.yaml` — AI context vars (or run `bun run agents:setup` for an interactive walkthrough)\n- `config/variables.ts` — runtime URLs for Playwright (`envDataMap`)\n\n### 2. Add Components\n\n```bash\n# Create a new page component\ntouch tests/components/ui/YourPage.ts\n\n# Create a new API component\ntouch tests/components/api/YourApi.ts\n```\n\nFollow patterns in `ExamplePage.ts` and `ExampleApi.ts`.\n\n### 3. Add Tests\n\n```bash\n# Create test directory\nmkdir tests/e2e/your-module\n\n# Create test file\ntouch tests/e2e/your-module/your-feature.test.ts\n```\n\n### 4. Generate Context\n\nLoad the `/project-discovery` skill in your AI assistant to generate project-specific context (the domain map inside `business-domain-context` and the infra map inside `infra-context`), then `project-context` for the business maps (HTML inside `business-data-context`, `business-api-context` and `business-e2e-context`). Every map is read with `bun run context:map <slug>`; the list of map skills is `CONTEXT_MAP_SKILLS` in `cli/lib/context-maps.ts`. The Master Test Plan is written by `project-context` mode `test-plan` to the `QA Master Test Plan` Epic in Jira.\n\n### 5. Adapt the Framework\n\nOnce the context maps exist (the domain, infra and `business-data-context` maps, plus `.context/project-config.md`; `bun run context:map <slug> --list` proves each one), run `/test-framework-adaptation` to wire the KATA architecture to your stack — auth, variables, OpenAPI facades, CI workflows, and the MCP registry. It runs an idempotent flow (no writes before your approval) and, on re-run, reports a GENERIC / ADAPTED checklist of what is still example-project boilerplate. After it passes, you can start writing automated tests.\n\n<br />\n\n## Companion repo\n\nThe development side lives in [agentic-dev-boilerplate](https://github.com/upex-galaxy/agentic-dev-boilerplate) — project foundation, design system, sprint development, deploys. Same `.agents/` variable system, same `agentskills.io` layout. Pair them or use one.\n\n<br />\n\n## Multi-harness architecture: one source, three consumers\n\nThis repo runs on **Claude Code, OpenCode, and Codex (CLI + Desktop)**. A project declares the harnesses it uses in `harnesses:` (`.agents/project.yaml`); every gate checks only those, and the boilerplate itself always checks all three (ADR-0012). There is exactly one copy of every instruction and every skill. Where the harnesses genuinely differ (MCP file format, hook API, how a skill is invoked) each keeps a thin versioned adapter. Nothing is duplicated.\n\n> Visual walkthrough, including what happens when you update a project created before this change: [**Una fuente, tres harnesses**](https://upex-galaxy.github.io/agentic-qa-boilerplate/harnesses.es.html) (Spanish, published page with diagrams). The dev boilerplate publishes its own release page, with a parity table against this repo: [agentic-dev-boilerplate: harnesses](https://upex-galaxy.github.io/agentic-dev-boilerplate/harnesses.es.html).\n\n| Surface | Claude Code | OpenCode | Codex CLI + Desktop |\n| ------- | ----------- | -------- | ------------------- |\n| **Instructions** | `CLAUDE.md` → `@AGENTS.md` **[generated shim]** | `AGENTS.md` (native) | `AGENTS.md` (native) |\n| **Skills** | `.claude/skills` **[generated alias]** | `.agents/skills/` (native) | `.agents/skills/` (native) |\n| **Commands** | none: `/<skill> <mode>` through `.claude/skills` | none: name the skill and the mode in prose | none: name the skill and the mode in prose |\n| **Hook** | `.claude/settings.json` → `UserPromptSubmit` + `SessionStart` (`compact`, `clear`) + `PostToolUse` (route pending, doc contracts) | `.opencode/plugins/personality-reinject.js` | `.codex/hooks.json` → `UserPromptSubmit` + `SessionStart` (`compact`, `clear`) + `PostToolUse` (doc contracts) |\n| **MCP** | `.mcp.json` | `opencode.jsonc` | `.codex/config.toml` |\n\n- **Instructions.** `AGENTS.md` plus the section files it routes to under `.agents/instructions/` are the only instruction body (progressive disclosure: `AGENTS.md` is the always-on layer, each section loads when its ROUTER row or a hook `ROUTE:` line names it; see `.agents/instructions/README.md`). Edits go through `/framework-development` mode `instructions`; `instructions:check` locks the ROUTER behind an ADR and scores the router eval on every run, and `bun run instructions:audit` measures from local transcripts how often a routed section was actually read (ADR-0013). OpenCode and Codex load `AGENTS.md` natively; Claude Code loads `CLAUDE.md`, which is exactly `@AGENTS.md` plus one newline: a documented import rather than a symlink, so it survives a Windows checkout. Operational prose in the shim is structural drift, and `agents:compat:check` fails on it.\n- **Skills.** Every committed skill lives in `.agents/skills/`, and the project-level community skills install into the same store. OpenCode and Codex read it directly; Claude Code reaches it through `.claude/skills`, a POSIX symlink (Windows junction) that is generated and gitignored: never committed, never hand-edited. Each skill still declares the hosts it supports in its `compatibility:` frontmatter per the [agentskills.io](https://agentskills.io) spec, and hosts without slash triggers auto-activate from the same `description` field.\n- **Commands.** No harness gets generated command files: a skill is invoked by its name plus a mode ([Invoking a skill mode](#invoking-a-skill-mode)). A project command named like a skill would hide that skill's instructions, so the check fails on it and `bun run agents:compat` moves it aside.\n- **Hook.** `.agents/hooks/personality-reinject.mjs` is the one per-prompt emitter: the `AGENT IDENTITY:` line and one `ROUTE: read <file>` line per instruction section the prompt needs and the session has not read yet, capped at a few per prompt with the rest on one non-binding `ROUTE-OPTIONAL:` line. Claude and Codex run it as a command hook (plus `SessionStart` `compact` and `clear` hooks that re-arm the routes); OpenCode imports it from a thin plugin. On Claude Code a `PostToolUse` hook runs the same file and reminds the agent once when a routed section is still unread. A second hook, `.agents/hooks/doc-contracts.mjs`, runs after file edits in Claude Code and Codex: an edit inside a `LINT.IfChange` region gets one `DOCS:` line naming the pages that describe it (ADR-0016; silent outside the boilerplate's own repo, and OpenCode relies on the pre-push and CI gate).\n- **MCP.** Every server declared in `.mcp.json` must exist in the other configs in use with the same `.env` dependencies (when Claude Code is not in use, the first declared harness's file is the canonical set). Parity is checked semantically: each native format (JSON / JSONC / TOML) is normalized into a common shape, then compared on the `.env` variables each server depends on and on its literal settings, so a server missing from one host, or present in one host only, is a failure. The servers the boilerplate ships (`KNOWN_MCP_IDS` in `cli/lib/agent-compatibility-contracts.ts`) additionally get a strict per-host shape check when declared; a downstream project with a different set passes on the generic check alone. A local server that needs `.env` values starts through the same `.env` loader on all three hosts, and its `--filter` list is the dependency set compared; nothing beside the loader takes names from the host.\n\n### Regenerating and verifying\n\nBold `[generated]` cells above are output. Edit the source, then regenerate:\n\n| Generated artifact | Its source | Regenerate |\n| ------------------ | ---------- | ---------- |\n| `CLAUDE.md` (one-line `@AGENTS.md` shim) | `AGENTS.md` | `bun run agents:compat` |\n| `.claude/skills` (POSIX symlink / Windows junction) | `.agents/skills/` | `bun run agents:compat` |\n\n```bash\nbun run agents:compat         # regenerate every derived harness artifact, then check\nbun run agents:compat:check   # validate the whole contract (also runs in repo:check + pre-push)\n```\n\n**Project-owned slash commands** are plain harness command files the project writes and edits by hand (`.claude/commands/`, `.opencode/commands/`); nothing generates them and `bun sync` never overwrites them. The old overlay `.agents/compatibility/command-aliases.project.json` is inert: nothing reads it, and `bun sync` names it once in an informational row. One rule still applies: a command named like a repo skill (say `.claude/commands/sprint-testing.md`) would hide the skill's instructions, so `agents:compat:check` fails on it and `bun run agents:compat` (also run by `bun sync` and `bun setup`) moves it to `.backups/shadowing-commands/<same path>`, gitignored and recoverable.\n\n`agents:compat:check` covers, for the harnesses in use (a `Harnesses checked:` line, one `NOTE:` per skipped harness), the shim bytes, the alias target, no project command named like a skill, the hook adapters, MCP parity, and the eslint block wiring. It prints the alias status line on every run (created, OK, deferred until the migration commit, missing) and groups the errors per surface (`COMPATIBILITY_GROUP_ORDER` in `cli/lib/agent-compatibility.ts`), so \"alias pending commit\" and \"MCP drift\" never read as one flat failure. It runs inside `bun run repo:check`, in the pre-push hook, and conditionally in pre-commit. `bun run setup:doctor` reports the same surfaces for the same harnesses (the server count comes from `.mcp.json`) plus **Codex repository trust**: project `.codex/` config and hooks load only in a trusted repo, and that is runtime state no file read can verify.\n\nThe `.agents/` variable system is harness-agnostic and unchanged across all three.\n\n<br />\n\n## Contributing\n\n1. Load the `/test-automation` skill and read its `references/kata-architecture.md`\n2. Follow the automation standards referenced by that skill\n3. Use conventional commits\n4. Ensure all tests pass before PR\n\n<br />\n\n## License\n\nMIT — see [`LICENSE`](LICENSE).\n\n<br />\n\n---\n\n<div align=\"center\">\n\n<sub><b>You are here</b> — QA boilerplate repo overview for visitors · <b>Next</b>: <code>bunx create-agentic-qa@latest &lt;your-repo-name&gt;</code> to bootstrap · <code>bun run onboarding</code> for the visual repo tour · <a href=\"INSTALLER.md\"><code>INSTALLER.md</code></a> for installer details.</sub>\n\n</div>\n"}