testmd lets you write browser tests as Markdown files (_test.md) and commit them to your repo. On the first run, the AI agent authors each step and saves a recording. On every subsequent run, each step replays from cache with no LLM cost and much faster execution. Commit the test file and its recordings to git so teammates and CI can re-run the same tests without re-authoring.
Quick Start
Step 1: Create a test file. The filename must end in_test.md:
When to Use testmd vs run
kane-cli run is one-shot. It runs an objective, uploads results, and exits. It is ideal for quick, one-off verifications like checking if a page loads correctly or extracting a value from a live site.
kane-cli testmd run is for tests you want to persist. Use it when you are building a login flow smoke test, a regression suite, or any test you plan to re-run across builds. The test file lives in your repo, recordings are cached and committed alongside it, and every subsequent run replays from cache without consuming LLM credits. Teammates and CI pick up the same recordings and replay them identically.
If you run an ad-hoc objective with kane-cli run and later decide you want to keep it, use the --name flag to save it as a _test.md file (see Recording a Test from a Live Session below).
File Format
A_test.md file has four parts in order:
YAML Frontmatter
Configuration between--- markers at the top of the file:
Mobile Target
target: also accepts the two mobile values, emulator for a virtual Android device and simulator for a virtual iOS device, with the app under test as its own root key:
target:emulatorruns on an Android emulator,simulatoron an iOS simulator. The platform never appears separately, the target implies it.app: the app under test, required with a mobile target and rejected with a browser one. A build path (emulator.apk, simulator.zip) or an uploaded app id,APPfollowed by six or more digits. On-device package ids are not accepted.no_reset: optional. Keep the app’s existing state between runs instead of resetting it.device_name/os_version: optional per-test defaults for the device, in the vocabulary ofkane-cli devices list --target <kind>(local) orkane-cli devices list --target <kind> --remote(grid catalog). The run flags--device-name/--os-versionoverride them. A name needs a version.
kane-cli testmd run and in batch with testrun. Locally that needs macOS Apple Silicon, and setup is covered in Mobile Testing. With testrun run --remote, the suite runs on a grid emulator or simulator from any machine.
Root-only vs root-or-per-step
- Root only — Chrome settings (
target,chrome_profile,cdp_endpoint,ws_endpoint,headless), the mobile keys (app,no_reset),url,mode,tags, andon_lock_conflict. These apply to the whole run; setting them on an individual step is a parse error. - Root or per-step — everything else can appear in the per-step
yamlblock to override the frontmatter for that step only.
Title and Steps
After the frontmatter, add a# H1 title for the test. This is purely decorative. Kane CLI ignores everything before the first ## heading.
Each ## H2 heading marks a test step. The heading text is a label for your reference; the agent reads the step body underneath it. Write the body as either plain English prose describing what the agent should do, or a single @import <path> line to pull in a reusable helper file. Do not mix prose and @import in the same step.
Per-Step Config Overrides
You can override frontmatter settings for individual steps by adding ayaml fenced block immediately under the step heading:
optional: true tells Kane CLI that a failure on this step should not fail the overall test. The run continues to the next step.
optional
optional: true marks a step as soft-failing. If the step fails:
- The overall run does not halt.
- Subsequent steps still execute.
- The step is reported as failed in
Result.mdwith an(optional)suffix.
false, in which case any failure stops the run and marks the remaining steps as skipped.
optional is allowed on every prose step. On an @import step it is allowed only at the top level of a test file — see Composing tests with @import.
Step body shapes
The body of a step (everything after the optionalyaml block) must be exactly one of:
- A prose objective — one or more lines of natural language describing what the agent should do. A step body follows the same rules as any objective, so end it in a check of the result and phrase actions as goals — see Writing objectives.
- An
@import— a single line of the form@import <path>and nothing else.
@import in the same body is a parse error.
Replay and Cascade Rule
This is the most important concept intestmd.
Replay
After the first run, each step replays from its cached recording with no AI agent, no LLM cost, and much faster execution. A step replays only if all of these hold:- A recording for that step exists
- The step’s prose is unchanged since the recording
- The step’s
yamlblock is unchanged - No earlier step invalidated it
Cascade
Editing step N re-authors step N and every step after it. Each step starts where the previous step left off (URL, login state, open tabs). When step 3 changes, step 4 cannot safely replay against state that no longer exists. Useful commands:Reusing Flows with @import
Extract repeating flows (login, setup, cookie banner dismissal) into helper files:
- Helper filename must not end in
_test.md. Only_test.mdfiles are valid entry points - Path resolves relative to the importing file, not the shell’s working directory
- The step body must be exactly
@import <path>with no mixed prose or extra lines - The step’s
yamlblock may contain onlyoptional optional: trueon@importis allowed only at the root file level, not on nested imports- Variables and context propagate into helpers automatically
Editing a helper re-authors that step in every test that imports it, plus everything after the import in those tests. The same cascade rule applies.
Variables
Variables let you parameterise objectives with reusable values and secrets. Reference a variable inside any prose objective using its placeholder syntax (default{{name}}):
Defining variables in a test.md
Variables can be set in three places inside a_test.md file, in order of increasing specificity:
name: "value" works too — tester_email: "alice@example.com" is equivalent to tester_email: { value: "alice@example.com" }.
Secrets
Secrets are variables withsecret: true. They are masked in displayed output, redacted in Result.md, and routed to TestmuAI’s secrets store instead of being synced as plain Test Manager variables.
Context
Context files are plain Markdown text that the agent reads alongside your objective. Use them for standing instructions — coding conventions, accounts to use, sites to avoid.Recording a Test from a Live Session
Run an ad-hoc objective with the--name flag to save it as a replayable test file:
.testmuai/tests/amazon-search_test.md. Move that file into your repo and re-run it with testmd run.
The recorded file is a regular _test.md — it is parsed and executed exactly like a hand-written one, and you can edit it freely.
Without
--name, ad-hoc runs are ephemeral and nothing is written to disk.Commands
Full detail: Running test.md files.
Flags for testmd run
All kane-cli run flags apply (--agent, --headless, --max-steps, --timeout, --variables, etc.), plus these additional flags:
Flag values win over frontmatter for all settings except
variables. The file owns variables. You can add new keys via flags but cannot override file-defined ones.Output Directory
After a run, Kane CLI creates an output directory next to the test file:output-<stem>/ is commit-safe. Commit it to git so teammates and CI replay the same recordings.
For tests using @import, helper recordings land next to the helper file in helper-output-<helper>-<root>-<step>/ directories. These are also commit-safe.
Result.md
After each run,Result.md is generated with:
CI/CD Usage
Exit Codes
Common Parse Errors
Parse errors abort before any browser launch with exit code2:
Example: Full Test with Imports
tests/checkout_test.md:
tests/helpers/login.md:
Next Steps
- Writing Objectives: Learn how to write effective natural language objectives
- Variables & Context: Parameterize tests with variables, secrets, and context files
- Agent Mode: Parse structured NDJSON output from Kane CLI
- CI/CD Integration: Add Kane CLI to your pipeline
- CLI Reference: Full flag and command reference
- Troubleshooting: Debug common issues