Kensa

Reference

Data Format - How Kensa stores your tests on disk

Updated for v0.16.0Edit on GitHub

Kensa has no database. A project is just a folder, and everything in it is plain text you can read, grep, diff, and commit. This page documents the on-disk format so you know exactly what Kensa writes.

Project layout

my-tests-project/
├── .tms/
│   ├── schema.yaml          # case structure: system + custom field definitions
│   ├── config.yaml          # project settings (name, id format, next id, UI)
│   ├── badges.yaml          # optional: colored pill/emoji badge config
│   ├── attachments/
│   │   └── <case-id>/...     # files attached to a case
│   ├── shared-steps/*.md     # reusable step sequences
│   ├── plans/*.json          # test plans (case groupings)
│   ├── runs/*.json           # manual execution records
│   ├── tools/                # tool presets (e.g. mobile/presets.yaml)
│   └── trash/                # soft-deleted cases (recoverable)
├── suites/
│   ├── auth/
│   │   ├── 001.md
│   │   ├── 002.md
│   │   └── checkout/         # nested suite (folder)
│   │       └── 003.md
│   └── reports/
│       └── 004.md
├── CLAUDE.md                 # instructions for AI agents
├── .mcp.json                 # optional MCP server config
└── README.md
  • Suites are folders. Nesting is unlimited; the folder hierarchy is the suite hierarchy.
  • Cases are .md files named <id>.md (the filename stem always matches the frontmatter id). Kensa enforces and can repair this invariant.

A test case file

A case is Markdown with a YAML frontmatter block:

YAML
---
id: 218
title: Login with valid credentials
priority: high
status: active
tags: [auth, smoke, regression]
preconditions: |
  User is registered and email is verified.
  No active session exists.
custom:
  test_type: functional
  browser: chrome
  estimated_duration: 30
source_id: testrail:C12345
created_at: 2026-05-10T14:30:00Z
updated_at: 2026-05-10T16:45:00Z
---

## Steps

1. Open login page at /login
   - Expected: Login form is displayed with email and password fields
2. Enter valid email "user@example.com"
3. Enter valid password
4. Click "Login" button
   - Expected: User is redirected to /dashboard
   - Expected: User name is displayed in header

## Description

Additional context about the case.
  • System fields (title, priority, status, tags, preconditions, steps, …) live at the top level of the frontmatter.
  • Custom fields live under custom:.
  • Steps are an ordinary numbered Markdown list; expected results are - Expected: sub-bullets. A reviewer reading the raw file on GitHub sees a perfectly legible test case.
  • source_id records where an imported case came from (testrail:C12345, qase:1, csv:row-42) for traceability.

The schema (.tms/schema.yaml)

The schema defines the structure of your cases. As of the v2 format, system fields are part of the schema too (marked system: true), so the case form, the validators, and the CLI all introspect the same definitions.

YAML
version: 2
fields:
  - key: priority
    name: Priority
    type: select
    system: true
    options: [low, medium, high, critical]
    order: 0
  - key: test_type
    name: Test Type
    type: select
    required: false
    default: functional
    options: [functional, performance, security, accessibility]
    order: 10
  - key: browser
    name: Browser
    type: multiselect
    options: [chrome, firefox, safari, edge]
    order: 11
  - key: estimated_duration
    name: Estimated Duration (min)
    type: number
    default: 15
    order: 12

Field types: text, textarea, select, multiselect, number, date, checkbox, url.

Field properties: key (immutable identifier), name, type, required, default, description, options (for select/multiselect), order, and system.

The schema is versioned and migrations are non-destructive - see features/schema-and-fields.md.

Project config (.tms/config.yaml)

YAML
version: 1
project:
  name: My Tests Project
  description: ...
  id_format: numeric       # numeric | prefixed
  id_prefix: null          # required when id_format = prefixed
  next_id: 219
ui:
  default_view: list
  terminal_position: right # right | bottom
  • ID format is either numeric (001, 002, …) or prefixed (AUTH-001). The next_id counter is allocated atomically and reconciled against the highest id actually on disk, so concurrent git additions don't collide.

Badges (.tms/badges.yaml)

Optional, committed config that maps field values to colored pill or emoji badges so priority/status/custom-value chips render consistently for every contributor and for the CLI. No badges exist until you add the first one.

Trash

Deletes are soft. A removed case moves to .tms/trash/ (flat, with a timestamp suffix on collision) and can be restored from the UI or the CLI. The only hard delete is an explicit "empty trash" / kensa trash purge.

Why this format

  • Git-native. Cases diff cleanly; PRs review test changes; blame and history are free.
  • Tool-agnostic. Any editor, script, or AI agent can read and write cases - Kensa is not required to make sense of the files.
  • Byte-stable round-trip. The TypeScript app and the Rust kensa CLI both parse and re-serialize the format byte-for-byte identically, guaranteed by a shared parity test suite. Editing a case in either tool never produces noisy diffs.