Skip to content
Conduit

skill

agentic-qa-boilerplate

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

upex-galaxy/agentic-qa-boilerplatecommunitylistedunreviewednot running here yet

Set up

Skills are installed by placing their folder where your agent loads skills.

Install instructions come from the README

Skills do not use an MCP config. Follow the steps in the README below; one-click install arrives with accounts.

README

synced Oct 14, 2026

TypeScript Bun License: MIT

Get started in one command

bunx create-agentic-qa@latest <your-repo-name>

One command. Downloads · scrubs git history · renames the project · runs bun install · launches the installer, which opens a local page in your browser.

Prerequisites

Before 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.

Hard blockers (the preflight stops the run until they are fixed)

ToolMin versionWhyInstall
Bunthe floor cli/preflight.ts enforcesRuntime 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
Nodethe floor cli/preflight.ts enforcesHooks run node .agents/hooks/personality-reinject.mjs; checked by the scaffolder doctor and the bun setup preflightnodejs.org
An agent — Claude Code, OpenCode or Codexlatestthe 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 configureSee each project's official docs
gitanyScaffolder runs git init; pre-commit hooks (Husky) require gitgit-scm.com/downloads
taranyScaffolder extracts the template tarball. Either flavour works — GNU tar (Linux, WSL, Git Bash) or bsdtarShips with macOS, Linux, and Windows 10 1803+ / Windows 11 (C:\Windows\System32\tar.exe)

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.

Quasi-required (the preflight warns, the run goes on)

ToolMin versionWhyInstall
engramMIN_ENGRAM_VERSION in cli/install.tsPersistent 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

Per-skill CLIs (lazy-required — needed when the skill runs, not at setup)

These 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.

The 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).

Variables and MCP credentials (.env keys)

Each 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):

  • 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.
  • 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.
  • 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.

Nothing blocks install, update or setup:doctor: a value is validated by the code that reads it, with a named error (ADR-0005).

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.

.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.

When the installer tells you something is wrong

StageCheck depthBehavior
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.
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.
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.
bun run setup:doctorRe-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.

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.

Start here — pick your path

GoalWhat to read / run
Start a new project — magic command (recommended)bunx create-agentic-qa@latest <your-repo-name> — official scaffolder (npm)
Start a new project — GitHub "Use this template"Click Use this template → clone your new repo → bun install && bun setup (see Other ways to start)
Contribute to the boilerplate itselfgit clone … then bun install && bun setup (see Other ways to start)
Get oriented before installingbun run onboarding — opens the docs site on its "Empezar aquí" page (docs/core/empezar-aqui.html)
Understand the methodologybun run docs → Metodología (IQL, how this repo implements it); official site: upexgalaxy.com/metodologia
Browse the human docsbun run docs — local HTML site (setup guides, methodology, exploration); add your own pages under any docs/ folder except docs/core/
See what bun setup configuresINSTALLER.md — run bun run setup:doctor after setup
You're an AI agentAGENTS.md (auto-loaded each session on every supported harness; it routes to .agents/instructions/)

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.

What this is

A 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 — pair them or use one.

Scaffold a new project

create-agentic-qa is the official scaffolder (npm, source in packages/create-agentic-qa/). One command, full setup:

bunx create-agentic-qa@latest <your-repo-name>
cd <your-repo-name>

What it does:

  1. Downloads upex-galaxy/agentic-qa-boilerplate (latest main) as a tarball — no git history.
  2. Rewrites package.json name + .agents/project.yaml project.project_name (and project.project_key when --project-key is passed).
  3. Initializes a fresh git init -b main with an initial commit.
  4. Runs bun install.
  5. 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) — 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.

Useful flags (full list in packages/create-agentic-qa/README.md):

FlagEffect
--hereBootstrap into the current directory instead of a new one.
--template <ref>Pin to a branch / tag / SHA instead of main.
--template-repo <owner/repo>Use a fork instead of upex-galaxy/agentic-qa-boilerplate.
--project-key UPEXPre-fill the Jira project key (otherwise prompted).
--no-install / --no-setupSkip bun install or the installer (bun setup).
--non-interactiveAuto-pick defaults (also auto-detected when no TTY is present).

Then continue with the per-project workflow:

# Optional: open the docs site on its "Empezar aquí" page
bun run onboarding

# Optional, Claude Code only: configure the statusline in a SEPARATE terminal
bunx -y ccstatusline@latest

# Drive the QA lifecycle inside the agent:
/agentic-qa-onboard     # first-time orientation tour
/project-discovery      # reverse-engineer the target app into the domain + infra context maps
/test-framework-adaptation        # wire KATA to the target stack (auth, vars, CI, MCP) — run once after discovery
/shift-left-testing     # Shift-Left: pre-sprint AC refinement on backlog batch
/sprint-testing         # in-sprint manual QA per ticket (plan + execute + report)
/test-documentation     # TMS docs + ROI scoring (Candidate / Manual / Deferred)
/test-automation        # KATA Plan -> Code -> Review
/regression-testing     # CI execution + GO / CAUTION / NO-GO

Don't chain bun run onboarding && bun setup — the docs server is blocking and the chain deadlocks. Run them as separate steps.

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.

Launching the agent

.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:

claude        # Claude Code (or Claude Desktop)
opencode      # OpenCode (or its desktop app)
codex         # Codex CLI (or Codex Desktop)

Open 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.

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.

Nothing 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.

Use this template (GitHub)

Prefer 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:

  1. Click Use this template → Create a new repository on the boilerplate's GitHub page.

  2. Pick owner + name for your new repo, choose visibility, create.

  3. Clone YOUR new repo locally:

    git clone https://github.com/<your-org>/<your-repo>.git
    cd <your-repo>
    
  4. Install + configure:

    bun install
    bun setup        # Engram, skills, community skills, .env wiring, MCPs
    

    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.

  5. (Optional) Rename the project inside the codebase: edit package.json → name, and .agents/project.yaml → project.project_name.

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.

Manual clone (contributors)

Hacking on the boilerplate itself (skills, installer, scripts, docs)? Clone the repo directly:

# 1. Clone the repository
git clone https://github.com/upex-galaxy/agentic-qa-boilerplate.git
cd agentic-qa-boilerplate

# 2. Install dependencies
bun install

# 3. Install Playwright browsers
bun run pw:install

# 4. Copy env template
cp .env.example .env   # fill in the values, then restart the agent session

# 5. (Optional) Visual orientation — close tab + Ctrl-C when done.
bun run onboarding

# 6. Run the interactive setup (Engram, skills, MCPs, .env)
bun setup

# 7. Validate the install
bun run setup:doctor

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.

How it works

Skills 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.

Project 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.

Features

FeatureDescription
KATA ArchitectureKomponent Action Test Architecture for clean test organization
PlaywrightModern browser automation with auto-waiting and tracing
Allure ReportsRich test reports with history and trends
TMS SyncTest results pushed to Jira/Xray by bun run test:sync after the run (off by default: AUTO_SYNC)
Context EngineeringContext skills hold the AI's map of your app (bun run context:map); .context/ is a regenerable cache of Jira and the API spec
Skills-based WorkflowsAgent skills under .agents/skills/ drive the AI-assisted QA and automation flows
Multi-harnessOne 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)
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)
MCP IntegrationLocal servers declared once in .mcp.json and mirrored for each host; browser automation runs through /playwright-cli, with no browser MCP

Configuration

This boilerplate has two configuration systems that serve different consumers and must not be conflated:

SystemFileConsumerLoaded at
Runtime test config.env + config/variables.tsPlaywright runner, KATA components, bun run * scripts (jiraSync, env validate, etc.)Test execution time
AI context engineering.agents/project.yamlEvery supported harness (Claude Code, OpenCode, Codex) — used to resolve {{VAR}} references in skills, commands, and templatesAI session bootstrap

Both are needed. Skip neither.

(a) Runtime test config — .env

Copy .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.

Every variable also has a typed schema, read by varlock: .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:

bunx varlock load --agent      # exit 0 = every required item for your TEST_ENV is set
bunx varlock explain TEST_ENV  # where one value comes from
bun run vars:schema            # regenerate .env.core.schema after editing the manifest

The 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).

Quote any value that contains a # (PASSWORD="pass#word"): varlock cuts an unquoted value at the first #.

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.

(b) Runtime URLs — config/variables.ts

Update 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):

const envDataMap: Record<
  Environment,
  { base: string; api: string; user: { email: string; password: string } }
> = {
  local: {
    base: 'http://localhost:3000',
    api: 'http://localhost:3000/api',
    user: userCredentialsMap.local,
  },
  staging: {
    base: 'https://staging.yourapp.com',
    api: 'https://staging.yourapp.com/api',
    user: userCredentialsMap.staging,
  },
};

(c) AI context engineering — .agents/project.yaml

Every 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:

bun run agents:setup

This 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.

Run Tests

# Run all tests
bun run test

# Run with UI mode (recommended for development)
bun run test:ui

# Run specific test types
bun run test:e2e           # E2E tests only
bun run test:integration   # API tests only
bun run test:smoke         # smoke / @critical tests

Project Structure

├── tests/
│   ├── components/               # KATA Components Layer
│   │   ├── TestContext.ts        # Layer 1: Base utilities + faker
│   │   ├── TestFixture.ts        # Layer 4: Unified test fixture
│   │   ├── api/                  # API components
│   │   │   ├── ApiBase.ts        # Layer 2: HTTP client base
│   │   │   └── ExampleApi.ts     # Layer 3: Domain component
│   │   ├── ui/                   # UI components
│   │   │   ├── UiBase.ts         # Layer 2: Page base
│   │   │   └── ExamplePage.ts    # Layer 3: Domain component
│   │   └── steps/                # Reusable ATC chains (preconditions)
│   │
│   ├── e2e/                      # E2E test specs
│   │   └── module-example/       # Example module
│   ├── integration/              # API integration tests
│   │   └── module-example/       # Example module
│   ├── setup/                    # Test setup files
│   │   ├── global.setup.ts       # Global setup
│   │   └── ui-auth.setup.ts      # UI authentication
│   ├── data/
│   │   ├── fixtures/             # Static test data (JSON)
│   │   ├── types.ts              # Test data types
│   │   └── DataFactory.ts        # Dynamic data generation
│   ├── utils/                    # Test utilities
│   │   ├── decorators.ts         # @atc decorator
│   │   └── jiraSync.ts           # TMS synchronization
│   └── KataReporter.ts           # Terminal reporter
│
├── config/
│   ├── variables.ts              # Runtime env vars consumed by Playwright/KATA
│   └── validateTestEnv.ts        # Test environment validation
│
├── .context/                     # Script caches + the few files this repo owns (ignored by default; see .gitignore)
│   ├── ADR/                      # Test-architecture decision records (committed, append-only)
│   ├── project-config.md         # Project config written by /project-discovery (committed)
│   ├── reports/                  # Generated output (GITIGNORED except its README): test map, regression reports
│   └── PBI/                      # Per-ticket backlog items + the MTP cache (GITIGNORED Jira cache; `bun run context:hydrate`)
│
├── .agents/                      # Agentskills.io spec layout — the shared, harness-agnostic substrate
│   ├── project.yaml              # AI context vars (resolved as {{VAR}} by skills)
│   ├── jira-fields.json          # Jira custom-field catalog (synced by `bun run jira:sync-fields`)
│   ├── jira-required.yaml        # Required Jira custom-field manifest
│   ├── README.md                 # Variable conventions reference
│   ├── hooks/                    # personality-reinject.mjs (one emitter, three harness adapters) + doc-contracts.mjs (edit-time DOCS: line)
│   ├── instructions/             # AGENTS.md sections, loaded on demand by the ROUTER (agent-project.md = this project's own)
│   └── skills/                   # THE skill store — read by all three harnesses
│       └── <skill>/              # one folder per skill; the catalogue is REGISTRY.md (generated)
│
├── .claude/                      # Claude Code adapter — settings.json (hook) + generated skills alias
├── .opencode/                    # OpenCode adapter — plugins/personality-reinject.js
├── .codex/                       # Codex adapter — config.toml (MCP) + hooks.json. Shared by CLI and Desktop
│
├── .github/workflows/            # CI/CD pipelines, one file per workflow (see CI/CD Pipelines below)
│
├── docs/                         # Human docs site (`bun run docs`)
│   ├── index.html                # Portal (sidebar built from each page's meta)
│   ├── assets/                   # Shared docs.css / docs.js, IQL diagrams
│   └── core/                     # Shipped pages (synced); other folders are project-owned
│
├── packages/                     # Boilerplate-only (pruned from scaffolds): the npm scaffolder, the published decks site
│
├── cli/                          # install.ts, doctor.ts, update-boilerplate.ts consumed by bun scripts
│
├── playwright.config.ts          # Playwright configuration
├── INSTALLER.md                  # Contract for bun setup — what each installer layer does
├── AGENTS.md                     # AI memory, always-on layer: binding rules, behaviour and the ROUTER, loaded by all three harnesses
├── CLAUDE.md                     # One-line shim (`@AGENTS.md`) so Claude Code reaches it. Never holds prose
├── .mcp.json                     # MCP config — Claude Code
├── opencode.jsonc                # MCP config — OpenCode
└── package.json                  # Scripts and dependencies

KATA Architecture

This boilerplate implements KATA (Komponent Action Test Architecture).

Architecture Layers

TestContext (Layer 1)
    ↓ extends
UiBase / ApiBase (Layer 2) ← Helpers here
    ↓ extends
YourPage / YourApi (Layer 3) ← ATCs here
    ↓ used by
TestFixture (Layer 4) ← DI entry point
    ↓ used by
Test Files ← Orchestrate ATCs

Component Types

ComponentPurposeLocation
ApiHTTP interactionstests/components/api/
PageUI interactionstests/components/ui/
StepReusable ATC chainstests/components/steps/

Example Test

The ATC lives in a component and carries the Jira key; the spec only orchestrates it through a fixture ({ ui }, { api } or { test }):

// tests/components/ui/LoginPage.ts (the ATC: one complete mini-flow)
@atc('UPEX-101')
async loginSuccessfully(credentials: LoginCredentials): Promise<void> {
  await this.fillAndSubmitLoginForm(credentials);
  await expect(this.page).not.toHaveURL(/.*\/login.*/);
}

// tests/e2e/auth/login.test.ts (the spec)
import { test } from '@TestFixture';
import { config } from '@variables';

test.describe('UPEX-100: Login', () => {
  test('UPEX-101: should log in with valid credentials', async ({ ui }) => {
    await ui.login.goto();
    await ui.login.loginSuccessfully({
      email: config.testUser.email,
      password: config.testUser.password,
    });
  });
});

See the /test-automation skill (references/kata-architecture.md) for complete documentation.

Available Scripts

The 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.

Test Execution

The test* family in package.json: the whole suite, UI mode, the subsets by type or tag, retries and last-failed re-runs.

Reports

The Playwright report, the allure:* family and the TMS sync (test:sync), all in package.json.

Code Quality

Lint, 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).

Utilities

Browser install, test-environment validation and artifact cleanup: the small scripts near the top of package.json.

CLI Tools

The 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.

--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.

Keeping your project in sync with the boilerplate

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:

  1. Reads .template/boilerplate.lock.json (committed in your repo) to find the last-synced SHA per component
  2. Clones the template lazily (sparse checkout, only the dirs that get synced)
  3. Computes the exact list of changed files between your synced SHA and template HEAD
  4. Classifies each file: clean fast-forward, locally diverged, new upstream, deleted upstream, binary, or whitespace-only
  5. 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
  6. Regenerates the derived surfaces from their sources (bun run agents:compat, skills:registry, kata:manifest logic) and runs the project's own gates
  7. Closes with one "Estado por superficie" table and ONE parity prompt for your AI (details below)

Before 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). A synced file you edited that upstream also changed now asks (decide later, keep mine, take the new one) instead of being overwritten.

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).

FlagEffect
(none)Interactive flow, in the page or the terminal: pick areas, review files, resolve divergences, confirm deletions
--autoNon-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
--forceLike --auto, and also deletes files upstream removed (backup + --rollback still apply)
--interactive, -iKeeps the prompts even when stdin is not a terminal
--dry-runPreview 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
--strictExit 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
--no-gatesSkip the post-sync gates
--headless / --uiKeep the run in the terminal / open the page even where the rules say terminal
--rollbackRestore the most recent backup
--skill a,b / --listSync only the named skills / list the skills the template offers

Without 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.

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.

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).

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.

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.

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.

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.

bun sync                # interactive
bun sync --auto         # unattended, no deletions
bun sync --dry-run      # preview (parity table included)
bun sync --strict       # CI: exit 1 on a blocking parity finding
bun sync --rollback     # restore the latest backup

The .template/boilerplate.lock.json file is committable: commit it so your team and CI know exactly which template version each component is on.

CI/CD Pipelines

GitHub Actions Workflows

The 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.

Environment Secrets

The 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.

Every 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.

A 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.

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.

Skills

Workflow skills (auto-trigger)

The 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:

StageOwner skill
Shift-Left, pre-sprint on a backlog batch/shift-left-testing
Planning, Execution, Reporting: in-sprint manual QA/sprint-testing
Documentation: TMS test cases + ROI scoring/test-documentation
Automation: Plan → Code → Review on KATA/test-automation
Regression: regression / smoke / sanity and the GO / CAUTION / NO-GO verdict/regression-testing

Everything 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.

Reusable community skills (installed by bun setup)

These 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.

Skill tiers (T1–T4)

Every 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.

TierWhatLocationLoad behavior
T1Project-owned (this repo).agents/skills/Silent — load on trigger
T2Vendored (upstream, attribution kept).agents/skills/judgment-day/Silent on explicit trigger or host orchestrator citation
T2-optOptional SDD bundle (user-installed)~/.claude/skills/sdd-* only if present on the machineSilent inside /framework-development only — see anti-leak
T3Community project-levelInstalled by install.ts PROJECT_LEVEL_SKILLSSilent if matched by category
T4Community user-level (global)Installed by install.ts USER_LEVEL_SKILLSSilent when a doctrine or your standing instructions name it for the moment; otherwise ASK before load

T3 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.

Validation: bun run skills:check checks tier coherence (orphan categories, tier mismatches, missing sections, stale doc paths).

Invoking a skill mode

There 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").

The modes of each skill are in its ## Mode routing section; the skills themselves are listed in .agents/skills/REGISTRY.md.

Variables system

The .agents/ directory hosts the variable system every skill and command uses. The main forms:

SyntaxPurposeResolves from
{{VAR_NAME}}Static project value (flat or env-scoped).agents/project.yaml
{{environments.<env>.<var>}}Explicit cross-env reference.agents/project.yaml -> environments.<env>.<var>
<<VAR_NAME>>Session/runtime value (e.g. <<ISSUE_KEY>>)Computed by the calling prompt at runtime
{{jira.<slug>}}Jira custom field reference.agents/jira-required.yaml + .agents/jira-fields.json

See .agents/README.md for the full contract.

Validation scripts:

bun run vars:check         # Every {{VAR}} and {{jira.*}} reference resolves
bun run jira:sync-fields   # Discover Jira custom fields -> .agents/jira-fields.json
bun run jira:check         # Validate jira-required.yaml against jira-fields.json

TMS Integration (Jira/Xray)

Two TMS modalities are supported out of the box:

  • 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.
  • 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.

For how skills resolve [ISSUE_TRACKER_TOOL] and [TMS_TOOL] tags to concrete CLIs or MCPs, see .agents/instructions/agent-tool-resolution.md Tool Resolution.

Configuration

  1. Get Xray API credentials from Jira
  2. Add to .env:
XRAY_CLIENT_ID=your-client-id
XRAY_CLIENT_SECRET=your-client-secret
XRAY_PROJECT_KEY=YOUR-PROJECT
AUTO_SYNC=true

# Key of the Test Execution this run imports into: the RTR by default (created per
# regression run by /regression-testing, linked to the RTP), the sprint-close STR at
# sprint close; never a Test Plan key. Both live under the "QA Test Artifacts" epic.
# Leave it empty and every run mints a brand-new, unparented Test Execution instead.
STP_EXECUTION_KEY=YOUR-PROJECT-194
# Optional, local-only: the RTP key, so an Execution minted by the local fallback is
# at least linked to the plan. No workflow reads it.
RTP_KEY=YOUR-PROJECT-60

Sync Test Results

# Always a separate step, AFTER the Playwright process exits: reports/atc_results.json
# is written by KataReporter.onEnd(), so nothing inside the run can read it.
AUTO_SYNC=true bun run test
bun run test:sync

In CI the suite workflows do exactly that: a Sync Results to TMS step gated on AUTO_SYNC == 'true' runs right after the test step. The global teardown only prints the ATC coverage summary; it never syncs.

Link Tests to Test Cases

// The @atc decorator goes on the ATC method, never in the test title
@atc('UPEX-101')
async loginSuccessfully(credentials: LoginCredentials): Promise<void> {
  // ...
}

KataReporter collects each ATC's result under its Jira key into reports/atc_results.json, which bun run test:sync pushes to the TMS.

Customization Guide

1. Update Project Identity

Edit these files:

  • package.json — name, description, repository
  • 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
  • .agents/project.yaml — AI context vars (or run bun run agents:setup for an interactive walkthrough)
  • config/variables.ts — runtime URLs for Playwright (envDataMap)

2. Add Components

# Create a new page component
touch tests/components/ui/YourPage.ts

# Create a new API component
touch tests/components/api/YourApi.ts

Follow patterns in ExamplePage.ts and ExampleApi.ts.

3. Add Tests

# Create test directory
mkdir tests/e2e/your-module

# Create test file
touch tests/e2e/your-module/your-feature.test.ts

4. Generate Context

Load 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.

5. Adapt the Framework

Once 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.

Companion repo

The development side lives in agentic-dev-boilerplate — project foundation, design system, sprint development, deploys. Same .agents/ variable system, same agentskills.io layout. Pair them or use one.

Multi-harness architecture: one source, three consumers

This 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.

Visual walkthrough, including what happens when you update a project created before this change: Una fuente, tres harnesses (Spanish, published page with diagrams). The dev boilerplate publishes its own release page, with a parity table against this repo: agentic-dev-boilerplate: harnesses.

SurfaceClaude CodeOpenCodeCodex CLI + Desktop
InstructionsCLAUDE.md → @AGENTS.md [generated shim]AGENTS.md (native)AGENTS.md (native)
Skills.claude/skills [generated alias].agents/skills/ (native).agents/skills/ (native)
Commandsnone: /<skill> <mode> through .claude/skillsnone: name the skill and the mode in prosenone: name the skill and the mode in prose
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)
MCP.mcp.jsonopencode.jsonc.codex/config.toml
  • 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.
  • 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 spec, and hosts without slash triggers auto-activate from the same description field.
  • Commands. No harness gets generated command files: a skill is invoked by its name plus a 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.
  • 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).
  • 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.

Regenerating and verifying

Bold [generated] cells above are output. Edit the source, then regenerate:

Generated artifactIts sourceRegenerate
CLAUDE.md (one-line @AGENTS.md shim)AGENTS.mdbun run agents:compat
.claude/skills (POSIX symlink / Windows junction).agents/skills/bun run agents:compat
bun run agents:compat         # regenerate every derived harness artifact, then check
bun run agents:compat:check   # validate the whole contract (also runs in repo:check + pre-push)

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.

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.

The .agents/ variable system is harness-agnostic and unchanged across all three.

Contributing

  1. Load the /test-automation skill and read its references/kata-architecture.md
  2. Follow the automation standards referenced by that skill
  3. Use conventional commits
  4. Ensure all tests pass before PR

License

MIT — see LICENSE.


You are here — QA boilerplate repo overview for visitors · Next: bunx create-agentic-qa@latest <your-repo-name> to bootstrap · bun run onboarding for the visual repo tour · INSTALLER.md for installer details.

↑↓ navigate openesc close