gherkin-ai v3.2.2

Migrating from 2.x to 3.0

3.0 hardens gherkin-ai for team and CI use. Most projects need no changes: every gherkin-ai.config.json produced by 2.x versions that we tested validates unchanged. The breaking changes below matter mostly to automation and to MCP users.

Start by running:

ghk upgrade             # what this project needs, as a report; asks before changing anything
ghk auth status         # which provider, model and credential will be used

Assisted upgrade (ghk upgrade)

ghk upgrade checks the project against everything in this guide:

Area What it checks
Generated code The oldest Generated by gherkin-ai CLI vX header under outputDir.
Configuration Validates against the v3 schema and adds $schema. Also reports unknown keys and retired models.
CI / scripts GitHub, GitLab, Azure Pipelines, Jenkins, CircleCI, Bitbucket, package.json, Makefile, scripts/. It finds removed login flags, ghk auth used as login, converge --strict, --apply without --allow-unattended-writes, comparisons against exit code 1, and Node.js < 22.
MCP clients .mcp.json, .cursor/, .vscode/ and .windsurf/ entries that are unpinned or use a relative path.
Legacy files Plain-text keys in ~/.gherkin-ai/auth.json (never printed), .gherkin-ai/audit.log, .ghe/logs/.

With --apply, or after you confirm interactively, it:

  1. creates the branch ghk-upgrade-v<version> (or the one given with --branch). It refuses to run on a dirty working tree unless you pass --force;
  2. writes the automatic config fixes;
  3. regenerates every feature;
  4. deletes files from the previous generation that are no longer generated, but only if their checksum in .ghe/inventory.json proves they were never edited. Other leftovers are listed for review; --prune deletes them too;
  5. with --commit, commits on that branch.

Items marked [manual] (CI files, MCP clients, credentials) are reported with a hint and are never edited. ghk upgrade --check exits with code 4 while required changes are pending. --cli also checks the npm registry for a newer CLI. --json prints the full report.

To validate the configuration layers on their own: ghk config validate.

Credentials and login

2.x 3.0
ghk login --apiKey <key> (accepted, then silently ignored) Removed. Use the provider's environment variable, or ghk login --provider <name>, which stores the key in the OS keychain. For scripts: --api-key-stdin.
--token, --user, --endpoint, --server Removed. They produced a synthetic token and pointed at a server that does not exist.
ghk auth = alias of login ghk auth status shows the effective provider and credential source.
~/.gherkin-ai/auth.json No longer read for keys. The provider moves to ~/.gherkin-ai/config.json (llm.provider); a legacy provider in auth.json is still honored. You can delete the file.
The first key found in OPENAI_API_KEY, ANTHROPIC_API_KEY, GEMINI_API_KEY, … was sent to whatever provider was selected Keys are resolved per provider. A selected provider without its key fails with exit code 3, instead of a warning followed by an authentication error.

LLM configuration

Exit codes and output

Workspace handling

Agent-driven writes

Delivery risk

ghk quality, ghk autopilot and the MCP risk tool use the rebuilt risk engine:

The scorecard keeps blastRadius, testStrength, securitySensitivity, architectureDrift, overallRiskScore, riskLevel, requiresHumanApproval and factors, and adds basis, changedFiles, dimensions and approvalThreshold. Set the approval level with policy.risk.requireApprovalAt. New command: ghk risk.

Test sandbox

verify --docker now runs in the stack's toolchain image:

Tests that download dependencies need --docker-network bridge, or sandbox.network: "bridge". The image and limits can be set in sandbox.*.

MCP server

Web Studio

Open the URL printed by ghk web. It contains a session token, and API calls without the token are rejected. autopilot and --apply cannot be run from the browser.

Plugins

config.plugins is validated. Both ["my-plugin"] (2.x) and { "load": ["my-plugin"] } work. When plugins.allow is set, only listed plugins load. The undocumented beforeGenerate/afterGenerate hook manager (core/hooks) was removed; use the GherkinAIPlugin interface.

Telemetry and audit

.gherkin-ai/audit.log (plain text) is replaced by .gherkin-ai/audit.jsonl (structured JSON lines). Telemetry is written for every command, locally only. Disable it with telemetry.enabled: false or GHK_TELEMETRY_DISABLED=true.

Generated code

Every stack now renders the same kernel from the feature (aggregate, unit tests, HTTP contract test where there is an API, pending BDD steps bound to the .feature file) and is built and tested in CI. Generated projects are complete, buildable projects rather than loose snippets, so regenerate into a clean directory or branch and review the diff: many paths moved.

Stack What changed
NestJS NestJS 11 + Prisma 6.19 + Zod 4. Layout consolidated under src/ (src/prisma/prisma.service.ts, src/infrastructure/multitenancy/, src/sagas/). package.json declares dependencies and build/test scripts (Jest 30 + cucumber-js via tsx). Prisma schema includes OutboxMessage, ProcessedEvent, SagaInstance and tenantId. Domain queries go through prisma.tenant, a client extension that replaces the removed $use middleware. NestJS 12 (ES modules) and Prisma 7 are opt-in with stack.frameworkVersion / stack.ormVersion (ENTERPRISE.md §11).
Express New dedicated preset on Express 5 (previously fell back to the React/Playwright skeleton).
.NET Tests live in tests/<Namespace>.Tests/ (xUnit + Reqnroll); the root .csproj excludes tests/**, features/** and the Aspire AppHost, uses Npgsql.EntityFrameworkCore.PostgreSQL and adds RootNamespace. Integration tests are tagged Category=Integration.
Java Maven project (pom.xml, Spring Boot 3.3) with sources under src/main/java/com/example/<project>/; one public type per file.
Kotlin New Gradle (KTS) project; previously contracts only.
Python FastAPI and Django (DRF) are separate presets with pyproject.toml, pytest and pytest-bdd.
Go Go module with internal/ packages, chi router, godog; contracts moved to internal/contracts/.
PHP Laravel 13 (PHP ≥ 8.3, PHPUnit 12, Behat); Laravel 11 is blocked by Composer security advisories. Contracts moved to contracts/.
Ruby Rails 8 API with RSpec and Cucumber.
Elixir Phoenix project (Bandit, ExUnit); LiveView with frontendStack.framework: "phoenix-liveview".
Rust Axum 0.7 crate (requires a recent stable toolchain).
Frontends Generated as a separate project in ./frontend: React 19 + Redux Toolkit, Vue 3 + Pinia, Angular 22 (zoneless, NgRx Signals; the classic NgRx store mode was removed), Next.js 16, React Native (Expo 57), Flutter.
Contracts gRPC and GraphQL are opt-in: "contracts": { "grpc": true, "graphql": true }.

Runtime kernel. Every backend also gets the runtime kernel (RUNTIME-KERNEL.md):

Its integration tests are excluded from the default test command and run with the stack's integration command, for example npm run test:integration or mvn verify -Pintegration, given DATABASE_URL and AMQP_URL.

Frontend API clients send a W3C traceparent header. Pass traceparent: false (Dart: traceparent: () => null) to disable it, or a function to propagate an existing trace. Cross-origin deployments must allow the header in CORS.

AWS CDK (./infrastructure) is generated when infrastructure.awsCdk is true, or by default when stack.messaging is SQS or SNS.

Identifiers are derived from the feature name with diacritics removed (Creación de Pedidos → CreacionDePedidos). To choose a shorter name, tag the feature with @aggregate:Pedido (or @aggregate(Pedido)); every generator then emits PedidoAggregate, CreatePedidoCommand, pedido.aggregate.ts and so on. Localized features (# language: es) are parsed as Gherkin. Command names come from identifiers quoted in When steps ("CreateOrderCommand" → CreateOrder) or, for English features, from the step's verb; otherwise a single Process<Feature> command is generated.

Generated toolchains target currently supported runtimes: Node 24 for frontends, .NET 8, Java 17, Python 3.12, Go 1.22, PHP 8.3, Ruby 3.3, Elixir 1.17. The CLI itself requires Node.js 22.12 or later (Node 20 is end-of-life).