Metadata-Version: 2.4
Name: tdd-dsl
Version: 0.1.0
Summary: LLM-friendly TDD DSL for behavior implemented in more than one language — one contract, tests emitted for every side.
Author: Rosavera
License: MIT
Project-URL: Homepage, https://github.com/Rosavera-I/tdd-dsl
Project-URL: Repository, https://github.com/Rosavera-I/tdd-dsl
Project-URL: Issues, https://github.com/Rosavera-I/tdd-dsl/issues
Keywords: tdd,dsl,testing,codegen,pytest,vitest,polyglot
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Code Generators
Classifier: Topic :: Software Development :: Testing
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"

<div align="center">

<!-- Logo/Header -->
<h1>
  <code style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 0.15em 0.4em; border-radius: 8px; font-family: 'Fira Code', monospace;">
    tdd-dsl
  </code>
</h1>

<p><strong>🧪 The same rule, implemented twice, drifting apart.<br/>Write the contract once — emit the test for both sides.</strong></p>

<p>
  An LLM-friendly DSL for behavior that lives in more than one place:<br/>
  a policy in your Python backend <em>and</em> your TypeScript frontend, an MCP server <em>and</em> its clients.<br/>
  One contract emits idiomatic tests for each side — plus HTTP mock servers from the same file.
</p>

<!-- Badges -->
<p>
  <a href="#">
    <img src="https://img.shields.io/github/actions/workflow/status/Rosavera-I/tdd-dsl/ci.yml?style=flat-square&logo=github&label=build" alt="Build Status"/>
  </a>
  <a href="https://pypi.org/project/tdd-dsl/">
    <img src="https://img.shields.io/pypi/v/tdd-dsl?style=flat-square&logo=pypi&color=blue" alt="PyPI Version"/>
  </a>
  <a href="https://www.npmjs.com/package/@rosavera/tdd-dsl">
    <img src="https://img.shields.io/npm/v/@rosavera/tdd-dsl?style=flat-square&logo=npm&color=cb3837" alt="npm Version"/>
  </a>
  <a href="#">
    <img src="https://img.shields.io/pypi/pyversions/tdd-dsl?style=flat-square&logo=python&color=3776AB" alt="Python Versions"/>
  </a>
  <a href="#license">
    <img src="https://img.shields.io/badge/license-MIT-blue?style=flat-square&logo=open-source-initiative&logoColor=white" alt="License"/>
  </a>
</p>

<!-- Language Badges -->
<p>
  <img src="https://img.shields.io/badge/🐍_Python-3.11+-success?style=flat-square" alt="Python"/>
  <img src="https://img.shields.io/badge/%E2%9A%A1_TypeScript-5.0+-3178C6?style=flat-square&logo=typescript&logoColor=white" alt="TypeScript"/>
  <img src="https://img.shields.io/badge/☕_Java-17+-orange?style=flat-square&logo=java&logoColor=white" alt="Java"/>
  <img src="https://img.shields.io/badge/%F0%9F%94%B7_C%23-12+-512BD4?style=flat-square&logo=csharp&logoColor=white" alt="C#"/>
  <img src="https://img.shields.io/badge/%E2%98%95_Rust-stable-DEA584?style=flat-square&logo=rust&logoColor=black" alt="Rust"/>
  <img src="https://img.shields.io/badge/%F0%9F%90%80_Go-1.21+-00ADD8?style=flat-square&logo=go&logoColor=white" alt="Go"/>
  <img src="https://img.shields.io/badge/%E2%9A%94%EF%B8%8F_Odin-dev-black?style=flat-square" alt="Odin"/>
  <img src="https://img.shields.io/badge/%E2%9A%A1_Zig-0.11+-FF4F00?style=flat-square&logo=zig&logoColor=white" alt="Zig"/>
</p>

</div>

---

## 🎯 What this is (and isn't)

tdd-dsl is a uniquely shaped tool for a uniquely shaped problem: **the same behavior implemented
more than once, in more than one language, with nothing keeping the copies honest.** It earns its
keep in three places:

1. **Full-stack policy parity** — a small product repo where the pricing / permissions / validation
   rule exists in both the backend and the frontend. One `.tdd` contract emits pytest *and* Vitest
   suites from the same cases, so drift becomes a CI failure instead of a production surprise.
2. **Protocol contracts (MCP)** — `target vibrissa` emits golden-file JSON cases for
   [Vibrissa](https://github.com/JMoak/vibrissa), turning an MCP server's protocol surface into a
   reviewable, replayable contract. Emit locally, commit the JSON — the server's CI never needs Python.
3. **Agent-first workflows** — the contract is the artifact you review; the generated tests are
   artifacts you don't have to. An agent that writes a ten-line contract can't hallucinate an
   assertion into two hundred lines of test code across three languages.

If your logic lives in exactly one language and one place, **you don't need this** — just write
the pytest or Vitest test directly. The polyglot breadth (12 emitters) is there for when a second
implementation shows up, not a reason to adopt.

These use cases are unusually shaped today. They're also getting more common: coding agents make
maintaining parallel implementations cheap, and MCP makes cross-language contracts routine.
tdd-dsl is shaped for that world.

---

## ✨ Quick Demo

The billing rule below ships twice — once in the Python backend, once in the TypeScript
frontend (and the Java service tier, if you have one). Write the contract once:

```text
suite "Billing policy contract"
target python "billing_policy"
target typescript "billing-policy"
target java "com.example.BillingPolicy"

case "flags enterprise usage before charging":
  given input:
    {
      "account": {"plan": "team", "yearsActive": 1},
      "usage": {"projects": 91, "seats": 42}
    }
  when call "quoteSubscription"
  then equals:
    {
      "tier": "enterprise",
      "monthlyUsd": null,
      "requiresReview": true,
      "reason": "seat_count"
    }
```

Emit each side's test suite from the same cases — if the implementations disagree, one of these fails:

```bash
# Python with pytest
$ tdd-dsl emit --target python contract.tdd
✓ Generated billing_policy_test.py

# TypeScript with Vitest
$ tdd-dsl emit --target typescript contract.tdd
✓ Generated billing-policy.test.ts

# Java with JUnit 5
$ tdd-dsl emit --target java contract.tdd
✓ Generated BillingPolicyTest.java
```

Declare HTTP test doubles in the same contract:

```text
mock server "payment_gateway" as gateway:
  stub "successful payment":
    when request:
      {"method": "POST", "path": "/api/charge", "body": {"amount": 100, "currency": "USD"}}
    then return:
      {"status": 200, "body": {"id": "ch_123", "status": "succeeded"}}
```

```bash
$ tdd-dsl wire emit contract.tdd     # canonical WireIR JSON, consumable by any stub server
```

```python
from tdd_dsl import parse
from tdd_dsl.wire import InProcessProvider, document_to_wire_irs

wire_ir = document_to_wire_irs(parse("contract.tdd"))["gateway"]
provider = InProcessProvider()          # stdlib-only HTTP stub server, no Docker
url = provider.start(wire_ir)           # tests read TDD_WIRE_GATEWAY_URL
...
mismatches = provider.verify()
provider.stop()
```

---

## 🚀 Installation

> **Release status:** Packaging for PyPI (`tdd-dsl`), npm (`@rosavera/tdd-dsl`), and GitHub Release binaries is **scaffolded and ready**, but those releases have **not been published yet**. Until the first `v*` tag + PyPI/npm publish, install from source (below) or use the contributor fallbacks in [`docs/PACKAGING.md`](docs/PACKAGING.md).

```bash
# From source (works today)
git clone https://github.com/Rosavera-I/tdd-dsl.git
cd tdd-dsl
pip install -e .

# After the first publish (not available yet):
#   pip install tdd-dsl
#   npm install -D @rosavera/tdd-dsl
#   npx tdd-dsl emit --target typescript contract.tdd
```

Once published, the npm package will download a platform binary from GitHub Releases on install. Supported platforms, release checklist, and contributor fallbacks (`TDD_DSL_BIN`, `TDD_DSL_USE_PYTHON`): [`docs/PACKAGING.md`](docs/PACKAGING.md) and [`npm/README.md`](npm/README.md).

---

## 📖 Usage

### CLI Commands

```bash
# Validate a contract
tdd-dsl validate contract.tdd

# Emit tests for a target language
tdd-dsl emit --target python contract.tdd
tdd-dsl emit --target typescript contract.tdd
tdd-dsl emit --target java contract.tdd
tdd-dsl emit --target vibrissa --out-dir cases/ mcp-contract.tdd

# Run contract against local implementation (python, typescript, go, rust)
tdd-dsl run --target python --cwd ./my-project contract.tdd
tdd-dsl run --target go --cwd ./my-go-package contract.tdd

# Emit WireIR JSON from mock server blocks
tdd-dsl wire emit contract.tdd
tdd-dsl wire emit --out-dir wire/ contract.tdd
```

### Python API

```python
from tdd_dsl import parse, validate, emit

# Parse and validate
ast = parse("contract.tdd")
diags = validate(ast)

# Emit to Python
python_code = emit(ast, target="python")
print(python_code)
```

---

## 🌍 Language Support Matrix

| Language | Emitter | Framework | Status | Runner |
|----------|---------|-----------|--------|--------|
| 🐍 Python | `python` | pytest | ✅ Stable | ✅ |
| ⚡ TypeScript | `typescript` | Vitest | ✅ Stable | ✅ |
| 🐹 Go | `go` | testing | ✅ Stable | ✅ |
| 🦀 Rust | `rust` | std test | ✅ Stable | ✅ |
| ☕ Java | `java` | JUnit 5 | ✅ Stable | ⏳ |
| 🔷 C# | `csharp` | xUnit | ✅ Stable | ⏳ |
| ⚔️ Odin | `odin` | core:testing | ✅ Stable | ⏳ |
| 🌙 Lua | `lua` | busted | ✅ Stable | ⏳ |
| 💎 Ruby | `ruby` | RSpec | ✅ Stable | ⏳ |
| 🍎 Swift | `swift` | XCTest | ✅ Stable | ⏳ |
| 🎯 Kotlin | `kotlin` | JUnit 5 | ✅ Stable | ⏳ |
| ⚡ Zig | `zig` | std.testing | ✅ Stable | ⏳ |
| 🔌 Vibrissa | `vibrissa` | MCP case JSON | ✅ Stable | emit-only → `vib run` |

Runner support means `tdd-dsl run --target <lang>` executes the generated tests against your local
implementation (Go needs a `go.mod` package dir, Rust a Cargo crate). All other targets are emit-only.

---

## Ecosystem

The same DSL grammar authors **library** contracts (Python / TypeScript / …) and **MCP protocol** contracts (`target vibrissa`).

- Library units: emit language tests and run with pytest / Vitest / etc.
- MCP protocol: emit Vibrissa JSON (`--out-dir` for one file per case) and run with `vib run`.
- MCP server CI should run Vibrissa against committed case JSON — **do not** install Python / tdd-dsl in that gate. Regenerate cases locally when `.tdd` changes (PyPI, `@rosavera/tdd-dsl`, or a source checkout).

Distribution (PyPI + npm binaries): [docs/PACKAGING.md](docs/PACKAGING.md). See also [docs/emitters/vibrissa.md](docs/emitters/vibrissa.md).

---

## 🏗️ Architecture

```
┌─────────────┐    ┌──────────────┐    ┌─────────────┐
│   .tdd      │───▶│   Parser     │───▶│     AST     │
│  Contract   │    │  + Validate  │    │   + Diags   │
└─────────────┘    └──────────────┘    └──────┬──────┘
                                              │
                       ┌──────────────────────┼──────────────────────┐
                       ▼                      ▼                      ▼
                ┌─────────────┐        ┌─────────────┐        ┌─────────────┐
                │ PyEmitter   │        │ TSEmitter   │        │ JavaEmitter │
                │  (pytest)   │        │  (Vitest)   │        │  (JUnit 5)  │
                └─────────────┘        └─────────────┘        └─────────────┘
```

---

## 🧪 Development

```bash
# Run tests
PYTHONPATH=src python -m unittest discover -s tests

# Update golden fixtures (intentional emitter changes only)
PYTHONPATH=src TDD_DSL_UPDATE_GOLDENS=1 python -m unittest tests.test_golden_fixtures

# Validate a contract
PYTHONPATH=src python -m tdd_dsl validate tests/fixtures/valid_minimal.tdd
```

---

## 📚 Documentation

| Document | Description |
|----------|-------------|
| [docs/README.md](docs/README.md) | Overview & quickstart |
| [docs/SPEC.md](docs/SPEC.md) | DSL specification & grammar |
| [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) | System architecture |
| [docs/TICKETS.md](docs/TICKETS.md) | Development backlog |

**Emitter Docs:**
- [Python/pytest](docs/emitters/python.md)
- [TypeScript/Vitest](docs/emitters/typescript.md)
- [Java/JUnit 5](docs/emitters/java.md)
- [Kotlin/JUnit 5](docs/emitters/kotlin.md)
- [C#/xUnit](docs/emitters/csharp.md)
- [Rust](docs/emitters/rust.md)
- [Go](docs/emitters/go.md)
- [Swift/XCTest](docs/emitters/swift.md)
- [Lua/busted](docs/emitters/lua.md)
- [Ruby/RSpec](docs/emitters/ruby.md)
- [Odin](docs/emitters/odin.md)
- [Zig](docs/emitters/zig.md)
- [Vibrissa](docs/emitters/vibrissa.md) (MCP case JSON)

---

## 🤝 Contributing

Contributions welcome! The project follows a test-first approach:

1. **Issues first** — Check [docs/TICKETS.md](docs/TICKETS.md) for backlog
2. **Test-first** — Add fixtures before implementation
3. **Golden fixtures** — Update intentionally via `TDD_DSL_UPDATE_GOLDENS=1`
4. **Mutation tests** — Include failure cases, not just happy paths

```bash
# Setup dev environment
git clone https://github.com/Rosavera-I/tdd-dsl.git
cd tdd-dsl
pip install -e ".[dev]"

# Run the test suite
pytest tests/

# Or with unittest
python -m unittest discover -s tests

# npm wrapper smoke (Python fallback; no release binary required)
npm run smoke --prefix npm
```

Release binaries and npm/PyPI publishing: [docs/PACKAGING.md](docs/PACKAGING.md).

---

## 📄 License

MIT © [Rosavera](https://github.com/Rosavera-I)

---

<div align="center">

<p>
  <sub><sup>Made with 🌹 and 🧪</sup></sub>
</p>

<p>
  <a href="https://github.com/Rosavera-I/tdd-dsl/stargazers">⭐ Star</a> •
  <a href="https://github.com/Rosavera-I/tdd-dsl/fork">🍴 Fork</a> •
  <a href="https://github.com/Rosavera-I/tdd-dsl/issues">🐛 Issues</a>
</p>

</div>
