QA contracts for the vibe coding era.
Protect your pages, features, and APIs from unintended changes β even when AI is writing the code.
Inspired by data contracts in data engineering.
When vibe coding with AI assistants, things break silently. The AI adds a feature and accidentally removes a form field. It refactors a component and changes the API shape. It "improves" a page and breaks the user flow.
You don't notice until production.
Themis introduces QA contracts β human-readable documents that define what a page, feature, or API endpoint is and does. AI assistants read these contracts before making changes and stop when something would break.
You: "Add dark mode to the login page"
AI: β οΈ Themis: This change would break a locked contract.
Contract: themis-qa/pages/auth/login/login.contract.md
Clause: Visual #3 β "Sign in primary button below fields"
Your change removes the primary button styling.
1. Find another approach
2. /themis-amend to update the contract
βββββββββββββββββββββββ
β Shaping Inputs β
β β
β - Prompts β
β - Briefs β
β - Screenshots β
β - Narratives β
ββββββββββ¬βββββββββββββ
β
/themis-generate
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β themis-qa/ β
β β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββββββββββ β
β β INDEX.md β β config.md β β global/ β β
β β (routing β β (platform, β β auth-flow β β
β β table) β β settings) β β navigation β β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββββββββββ β
β β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββββββββββ β
β β pages/ β β api/ β β components/ β β
β β auth/ β β auth/ β β data-table β β
β β login/ β β login/ β β modal β β
β β dashboard/ β β post.md β β β β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββββββββββ β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
ββββββββββββ ββββββββββββ
β β
βΌ βΌ
βββββββββββββββββββββββ ββββββββββββββββββββββββ
β Layer 1: AI Guard β β Layer 2: CI Safety β
β β β β
β AI reads contracts β β /themis-check runs β
β before changes. β β against PR diff. β
β Stops on violation.β β Fails build on β
β β β critical violations.β
βββββββββββββββββββββββ ββββββββββββββββββββββββ
Contracts are .contract.md files β Markdown with YAML frontmatter. Human-readable, git-friendly, AI-native.
---
name: Login Page
description: User authentication via email and password
type: page
path: /auth/login
status: locked
priority: critical
platform: web
references:
- api/auth/login/post.contract.md
created: 2026-03-15
updated: 2026-03-15
---
## Visual
- Login form centered on page
- Fields: email (text input), password (password input)
- "Sign in" primary button below fields
- "Forgot password?" link below button
## Behavior
- Submitting with valid credentials redirects to `/dashboard`
- Submitting with invalid credentials shows inline error
- Empty fields show validation on blur
## Data
### Request
- `POST /auth/login`
- Body: `{ email: string, password: string }`
### Response
- 200: `{ token: string, user: { id, email, name } }`
- 401: `{ error: "invalid_credentials" }`
## Rules
- MUST NOT remove or rename form fields
- MUST NOT change the authentication endpoint
- MUST preserve tab navigation order
- MAY update styling if visual layout is maintained| Section | What it captures | Example |
|---|---|---|
| Visual | What it looks like | "Login form centered, two fields, one button" |
| Behavior | What it does | "Invalid credentials show inline error" |
| Data | What it sends/receives | "POST /auth/login with email + password" |
| Rules | Hard constraints (RFC 2119) | "MUST NOT remove form fields" |
βββββββββββ lock βββββββββββ violation βββββββββββββββ
β draft ββββββββββββββββΆβ locked βββββββββββββββββΆβ /themis-amendβ
βββββββββββ βββββββββββ ββββββββ¬βββββββ
β² β
β approve β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
- draft β Contract is being shaped. Not enforced.
- locked β Contract is active. AI and CI enforce it.
- deprecated β Contract is retired. Skipped during checks.
Contracts are organized by scope, similar to how data contracts work in data engineering:
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β GLOBAL β
β Cross-cutting: auth flows, navigation, error handling β
β Applied everywhere. Always checked. β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β PAGE / FEATURE β
β Specific pages or user-facing features β
β Organized by URL path (web) or route (mobile) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β COMPONENT β
β Reusable UI elements with their own contracts β
β Referenced by page contracts β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Themis is a Claude Code plugin. Everything runs as skills β no separate CLI, no build step.
Interactive setup. Asks your platform, scaffolds the themis-qa/ directory, generates config, and wires Themis into your CLAUDE.md.
> /themis-init
Themis: What platform(s) does this project target?
1. Web 2. Mobile 3. API 4. Universal
You: 1
Themis: What's your local dev URL?
You: http://localhost:3000
Themis: β Created themis-qa/ with config, INDEX.md, and directory structure.
Use /themis-generate to create your first contract.
Create contracts from anything β a prompt, a shaping document, a screenshot, or a mix.
> /themis-generate
Themis: What are we contracting? (page, api, component, feature, global)
You: page
Themis: Describe it, paste a brief, or reference a file/screenshot.
You: Login page with email and password fields. Submits to POST /auth/login.
Redirects to /dashboard on success. Shows inline error on failure.
Themis: β Generated themis-qa/pages/auth/login/login.contract.md (draft)
Lock it?
You: yes
Validate your changes against all locked contracts. Run it before committing, or let CI run it on every PR.
> /themis-check
β
Themis: All contracts satisfied. 12 contracts checked, 0 violations.
> /themis-check
β οΈ Themis: Contract violation detected.
Contract: themis-qa/pages/auth/login/login.contract.md
Clause: Behavior #2 β "Submitting with invalid credentials shows inline error"
Priority: critical
What changed: Replaced inline error div with toast notification.
What breaks: Contract specifies inline error, not toast.
Options:
1. Revert this change and find another approach
2. /themis-amend to propose updating the contract
When a violation is intentional, amend the contract instead of reverting the code.
> /themis-amend
π Themis Amendment Proposal
Contract: themis-qa/pages/auth/login/login.contract.md
Current clause (Behavior #2):
"Submitting with invalid credentials shows inline error message"
Proposed clause:
"Submitting with invalid credentials shows toast notification with error"
Accept this amendment? yes
β
Contract amended and locked.
Already have a codebase? Scan it to auto-generate baseline contracts.
> /themis-scan
π Themis Scan Results
Found:
Pages: 12 API endpoints: 8 Components: 23
Recommended contracts (high-value targets):
β /auth/login β page with form + API call
β /dashboard β main page, complex layout
β POST /api/auth/login β auth endpoint
β DataTable β reused in 5 pages
Generate contracts for all, or select specific ones?
| Priority | AI-time | CI |
|---|---|---|
critical |
Hard block | Fails build |
high |
Hard block | Fails build |
medium |
Warning + propose amend | Warning in PR comment |
low |
Silent | Info in PR comment |
Add Themis to your GitHub Actions:
# .github/workflows/themis.yml
name: Themis Contract Check
on: [pull_request]
jobs:
themis:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Themis Contract Check
run: claude --skill themis-check --diff ${{ github.event.pull_request.base.sha }}Themis is stack-agnostic. The contract format works across platforms β only the directory structure adapts.
| Platform | Structure | Visual | Behavior | Data |
|---|---|---|---|---|
| Web | pages/ by URL path |
DOM, layout, CSS | User flows, navigation | API calls, forms |
| Mobile | screens/ by route |
Native components, gestures | Screen flows, transitions | API calls, state |
| API | api/ by endpoint |
N/A | Endpoint behavior | Request/response shapes |
| Universal | All of the above | Per-platform | Per-platform | Shared |
# In Claude Code
/plugin install themisThen in any project:
/themis-initThemis/
βββ .claude-plugin/ # Claude Code plugin config
β βββ plugin.json
β βββ hooks.json
βββ skills/ # Themis skills (the AI logic)
β βββ themis-init/
β βββ themis-generate/
β βββ themis-check/
β βββ themis-amend/
β βββ themis-scan/
βββ commands/ # User-facing slash commands
β βββ themis-init.md
β βββ themis-generate.md
β βββ themis-check.md
β βββ themis-amend.md
β βββ themis-scan.md
βββ templates/ # Templates used by /themis-init
β βββ themis.config.md
β βββ INDEX.md
β βββ contract.template.md
βββ docs/plans/ # Design docs
Contracts, not tests. Tests verify implementation. Contracts define intent. When you vibe code, the implementation changes constantly β but the intent shouldn't change without you knowing.
Human-readable first. Contracts are Markdown. Read them on GitHub, in your editor, in a PR review. No special tooling needed to understand what a page should do.
Defense in depth. AI reads contracts before coding (guardrail). CI checks contracts after coding (safety net). Two layers, one source of truth.
Amend, don't fight. When you intentionally change something, Themis doesn't block you β it asks you to update the contract. The contract evolves with your app.
Themis β divine law for your codebase.