Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ body:
- type: markdown
attributes:
value: |
Thanks for helping. Prefer `pip install mutiny-ai` in an agent project, or from source (`uv sync --extra dev`).
Thanks for helping. Prefer `pip install mutiny-ai` in an agent project, or from source (`uv sync --group dev`).
Security-sensitive reports → [SECURITY.md](https://github.com/CodewithJha/mutiny/blob/main/SECURITY.md).
- type: dropdown
id: area
Expand Down Expand Up @@ -43,7 +43,7 @@ body:
label: Reproduction steps
description: Minimal steps from a clean clone when possible.
value: |
1. `uv sync --extra dev`
1. `uv sync --group dev`
2. `cd examples/openai_support_agent`
3. …
validations:
Expand Down
2 changes: 1 addition & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
## Test plan

<!-- CI runs every job below. Record the local checks you ran and explain any
omissions; do not mark an unrun check as passed. Setup: uv sync --extra dev.
omissions; do not mark an unrun check as passed. Setup: uv sync --group dev.
See CONTRIBUTING.md for prerequisites, offline sample setup and details. -->

- [ ] Python lint: `uv run ruff check`
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
run: uv python install 3.11

- name: Sync workspace
run: uv sync --locked --extra dev
run: uv sync --locked --group dev

- name: Python lint
run: uv run --locked ruff check
Expand All @@ -60,7 +60,7 @@ jobs:
run: uv python install ${{ matrix.python-version }}

- name: Sync workspace
run: uv sync --extra dev
run: uv sync --group dev

- name: Unit tests
run: uv run pytest tests/unit -q --tb=short
Expand Down Expand Up @@ -94,7 +94,7 @@ jobs:
run: uv python install 3.12

- name: Sync workspace
run: uv sync --extra dev
run: uv sync --group dev

- name: Local CLI smoke (sample project)
working-directory: examples/openai_support_agent
Expand Down Expand Up @@ -159,7 +159,7 @@ jobs:
run: uv python install 3.12

- name: Sync workspace
run: uv sync --extra dev
run: uv sync --group dev

- name: Packaging unit tests
run: uv run pytest tests/unit/test_package_release.py -q --tb=short
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ Published on PyPI as **`mutiny-ai`** (+ [`mutiny-core`](https://pypi.org/project

### Fixed

- Use an unversioned workspace root and dependency groups (`uv sync --group dev`,
replacing `--extra dev`) instead of advertising a separate product version or
duplicate CLI entrypoint. Optional LLM dependencies use `--group llm`.
- Read the unreleased Hosted API's independent version from package metadata.
- Replace the stock web README with Hosted development instructions, align CLI
Python classifiers with CI (3.11/3.12), and state the source/workspace
`mutiny-api` requirement in `mutiny db` help.

- Reject `elite_count >= population_size` so every next generation has room
for mutations. Small populations must specify a smaller elite count.

Expand Down
6 changes: 3 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ nit. Larger adapters need an issue first.
2. **Sync** the workspace (contributors use source; users install `mutiny-ai`):

```bash
uv sync --extra dev
uv sync --group dev
uv run mutiny --help
uv run pytest tests/unit -q
```
Expand Down Expand Up @@ -44,7 +44,7 @@ Full detail: [README → Install](./README.md#install).
```bash
git clone https://github.com/CodewithJha/mutiny.git
cd mutiny
uv sync --extra dev
uv sync --group dev
uv run mutiny --help
```

Expand Down Expand Up @@ -123,7 +123,7 @@ PR CI (see `.github/workflows/ci.yml`) gates on Python **3.11** and **3.12**:
| Package build + isolated artifact gate | `uv run pytest tests/unit/test_package_release.py -q` then `./scripts/verify_release_artifacts.sh` |

```bash
uv sync --extra dev
uv sync --group dev
uv run ruff check # pinned correctness rules from pyproject.toml
uv run pytest tests/unit -q # default for most PRs
uv run pytest tests/integration -q # when touching API / Hosted / sample loop
Expand Down
19 changes: 14 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,13 +164,14 @@ pip install \
```bash
git clone https://github.com/CodewithJha/mutiny.git
cd mutiny
uv sync --extra dev
uv sync --group dev
uv run mutiny --help
```

### Contributors: editable `pip` (per package)

Root `pip install -e .` does **not** install the user CLI wheel (workspace root is not published). Install siblings together so deps resolve:
The root is an unversioned uv workspace, not an installable package. Use
`uv sync --group dev`, or install siblings together so dependencies resolve:

```bash
python3 -m venv .venv
Expand All @@ -183,6 +184,14 @@ mutiny --help

### Optional Hosted (API + UI)

The Hosted API is **unreleased**: its internal `mutiny-api` version is independent
of the published CLI/Core release. API logs, health responses, and headers read
that version from installed package metadata. `mutiny db` requires this
source/workspace package; `pip install mutiny-ai` does not include it.

Contributor dependencies now use `uv sync --group dev` (the root has no package
extras). Optional LLM dependencies use `uv sync --group llm`.

One command from the repo root:

```bash
Expand Down Expand Up @@ -256,8 +265,8 @@ With Hosted running (`./scripts/dev.sh`), use `mutiny run --hosted` for the API

| Symptom | What to check |
|---|---|
| `mutiny: command not found` | Run via `uv run mutiny …` from the repo after `uv sync --extra dev`, or activate the venv where you `pip install -e`’d the packages |
| `pip install -e .` at repo root fails | Expected — use `uv sync --extra dev` or the per-package editable installs above |
| `mutiny: command not found` | Run via `uv run mutiny …` from the repo after `uv sync --group dev`, or activate the venv where you `pip install -e`’d the packages |
| `pip install -e .` at repo root fails | Expected — use `uv sync --group dev` or the per-package editable installs above |
| Hosted UI won’t start | Install Node ≥ 20, then `cd apps/web && npm install` |
| Port already in use | Free **8000** (API) and **3000** (UI), or change the ports in `scripts/dev.sh` / Next |
| API looks down | `curl -sf http://127.0.0.1:8000/api/health` — expect JSON with a healthy status |
Expand Down Expand Up @@ -361,7 +370,7 @@ Full detail: [`docs/ROADMAP.md`](./docs/ROADMAP.md) · tracked as [🚀 Roadmap
PRs welcome — especially adapters, policy packs, tests, docs, and a11y. Newcomers: start with a [`good first issue`](https://github.com/CodewithJha/mutiny/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) (fork → PR in about 30 minutes).

```bash
uv sync --extra dev
uv sync --group dev
uv run pytest tests/unit -q
```

Expand Down
2 changes: 1 addition & 1 deletion SUPPORT.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Install is the same: `pip install mutiny-ai` then `mutiny init`.

## Before you open an issue

1. Confirm install path: `pip install mutiny-ai` in your agent project ([README install](./README.md#install)), or from source with `uv sync --extra dev`.
1. Confirm install path: `pip install mutiny-ai` in your agent project ([README install](./README.md#install)), or from source with `uv sync --group dev`.
2. Try the sample project: `examples/openai_support_agent/` with `uv run mutiny …`.
3. For Hosted: `curl -sf http://127.0.0.1:8000/api/health` and see [Troubleshooting](./README.md#troubleshooting).
4. Search existing issues for duplicates.
Expand Down
7 changes: 6 additions & 1 deletion apps/api/src/mutiny_api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
"""Mutiny Hosted API — orchestrates Core; never reimplements oracles."""

__version__ = "0.1.0"
from importlib.metadata import PackageNotFoundError, version

try:
__version__ = version("mutiny-api")
except PackageNotFoundError:
__version__ = "0.0.0+unreleased"
70 changes: 50 additions & 20 deletions apps/web/README.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,66 @@
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
# Mutiny Hosted UI

## Getting Started
Next.js frontend for Mutiny's Hosted lineage API. Customer campaigns execute
locally through `mutiny run`; `mutiny run --hosted` sends redacted results to
Hosted for viewing projects, campaigns, candidates, and regressions.

First, run the development server:
## Local development

Requires Python >= 3.11, uv, and Node.js >= 20 with npm. From the repository root:

```bash
uv sync --group dev
cd apps/web
npm ci
cd ../..
```

Start the API in one terminal, from the repository root:

```bash
uv run python -m mutiny_api --host 127.0.0.1 --port 8000
```

Start the UI in another terminal, from `apps/web`:

```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
On Windows PowerShell, use this alternative to the npm script's POSIX environment
assignment:

You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
```powershell
$env:WATCHPACK_POLLING = 'true'
npx next dev --hostname 127.0.0.1 --port 3000
```

This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
Open <http://127.0.0.1:3000>. Check API health at
<http://127.0.0.1:8000/api/health>. With dependencies installed, POSIX shells can
also start both processes using `./scripts/dev.sh` from the repository root.

## Learn More
## API connection

To learn more about Next.js, take a look at the following resources:
`next.config.ts` forwards `/api/*` to `MUTINY_API_URL`, defaulting to
`http://127.0.0.1:8000`. Set it on the web process before starting Next.js if the
API runs elsewhere. When the API uses `MUTINY_API_TOKEN`, set the same token on
the web process; middleware adds the bearer header for API requests. Keep this
token server-side. Non-loopback API binds require a token.

- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
The Hosted API is **unreleased**. Its internal `mutiny-api` metadata version is
independent of the published CLI/Core version. Source checkout or workspace
installation supplies the API; `pip install mutiny-ai` supplies the customer CLI.

You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
## Checks

## Deploy on Vercel
From `apps/web`:

The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
```bash
npm run lint
npx tsc --noEmit
npm run build
```

Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
See [CONTRIBUTING.md](../../CONTRIBUTING.md),
[Cold start](../../docs/COLD_START.md), and [SECURITY.md](../../SECURITY.md)
for the full contributor workflow and Hosted operating requirements.
7 changes: 6 additions & 1 deletion docs/CLI.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Mutiny CLI

`mutiny db` requires the unreleased Hosted `mutiny-api` package supplied by
the source/workspace installation (`uv sync --group dev`). It is not included
by `pip install mutiny-ai`; local `init`, `run`, and `test` do not require it.

Console script from the **`mutiny-ai`** PyPI package (`pip install mutiny-ai`).
Source of truth for flags: `mutiny --help` and `mutiny <cmd> --help`.

Expand All @@ -15,7 +19,8 @@ options:

positional arguments:
{db,init,run,test}
db Operator tools for Hosted SQLite lineage data (backup / restore)
db Hosted SQLite operator tools (requires mutiny-api from the
source/workspace)
init Scaffold .mutiny/adapter.py, policy.yaml, mutiny.yaml
run Load adapter + policy and start a campaign
test Replay project regressions under .mutiny/tests/
Expand Down
2 changes: 1 addition & 1 deletion docs/COLD_START.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ LLM key is **optional**. Demo pin defaults to template mutation fallback (determ
## 2. Install (monorepo — supported)

```bash
uv sync --extra dev
uv sync --group dev
uv run mutiny --help
# Hosted UI only:
cd apps/web && npm install && cd ../..
Expand Down
4 changes: 2 additions & 2 deletions examples/openai_support_agent/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ codes, so Mutiny can discover real `issue_refund(..., approved=false)` calls.
From the Mutiny repo root (uv workspace):

```bash
uv sync --extra dev
uv sync --group dev
cd examples/openai_support_agent
```

Expand All @@ -41,7 +41,7 @@ export MUTINY_SAMPLE_OFFLINE=1

## Mutiny (recommended)

From the Mutiny repo after `uv sync --extra dev`, always prefer `uv run mutiny …`
From the Mutiny repo after `uv sync --group dev`, always prefer `uv run mutiny …`
so the workspace CLI is on your PATH.

```bash
Expand Down
2 changes: 1 addition & 1 deletion packages/mutiny_cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ From the monorepo root (editable):

```bash
pip install -e packages/mutiny_core -e packages/mutiny_openai_agents -e packages/mutiny_cli
# or: uv sync --extra dev
# or: uv sync --group dev
```

</details>
Expand Down
1 change: 0 additions & 1 deletion packages/mutiny_cli/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ classifiers = [
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Topic :: Software Development :: Testing",
]
dependencies = [
Expand Down
7 changes: 6 additions & 1 deletion packages/mutiny_cli/src/mutiny_cli/db_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ def add_db_parser(subparsers: argparse._SubParsersAction) -> None:
"db",
help=(
"Operator tools for Hosted SQLite lineage data "
"(backup / restore; not a customer project API)"
"(backup / restore; requires mutiny-api from the source/workspace)"
),
description=(
"Hosted SQLite operator tools. Requires mutiny-api from the "
"source/workspace (uv sync --group dev); pip install mutiny-ai "
"does not include mutiny-api."
),
)
db_sub = db_p.add_subparsers(dest="db_command", required=True)
Expand Down
19 changes: 3 additions & 16 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,31 +1,18 @@
[project]
name = "mutiny"
version = "0.1.0"
description = "Mutiny — behavioral fuzz-testing engine for AI agents"
requires-python = ">=3.11"
dependencies = [
# This is an unversioned workspace, not an installable product distribution.
[dependency-groups]
dev = [
"mutiny-core",
"mutiny-openai-agents",
"mutiny-ai",
"demo-agent",
"mutiny-api",
]

[project.optional-dependencies]
dev = [
"pytest>=8.0",
"pytest-asyncio>=0.24",
"ruff==0.17.0",
]
llm = [
"mutiny-core[llm]",
]
openai-agents = [
"mutiny-openai-agents",
]

[project.scripts]
mutiny = "mutiny_cli.main:main"

[tool.uv.sources]
mutiny-core = { workspace = true }
Expand Down
5 changes: 5 additions & 0 deletions tests/unit/test_package_release.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@ def test_built_wheels_contain_current_surface(tmp_path: Path) -> None:
names = set(z.namelist())
assert "mutiny_cli/db_cmd.py" in names
assert "mutiny_cli/run_cmd.py" in names
metadata_path = next(n for n in names if n.endswith(".dist-info/METADATA"))
metadata = z.read(metadata_path).decode("utf-8")
assert "Classifier: Programming Language :: Python :: 3.13" not in metadata
assert "Requires-Dist: mutiny-api" not in metadata
assert not any(n.startswith("mutiny_api/") for n in names)
run_src = z.read("mutiny_cli/run_cmd.py").decode("utf-8")
# Compile catches SyntaxError class of stale release (historical PyPI gap)
ast.parse(run_src)
Expand Down
Loading
Loading