tazuna 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. tazuna-0.3.0/.cursor/rules/project.mdc +61 -0
  2. tazuna-0.3.0/.github/workflows/ci.yml +28 -0
  3. tazuna-0.3.0/.github/workflows/release.yml +54 -0
  4. tazuna-0.3.0/.gitignore +18 -0
  5. tazuna-0.3.0/AGENTS.md +57 -0
  6. tazuna-0.3.0/CHANGELOG.md +82 -0
  7. tazuna-0.3.0/CLAUDE.md +64 -0
  8. tazuna-0.3.0/CONTRIBUTING.md +38 -0
  9. tazuna-0.3.0/LICENSE +21 -0
  10. tazuna-0.3.0/PKG-INFO +141 -0
  11. tazuna-0.3.0/PROJECT.md +55 -0
  12. tazuna-0.3.0/README.md +115 -0
  13. tazuna-0.3.0/SECURITY.md +15 -0
  14. tazuna-0.3.0/docs/architecture.md +68 -0
  15. tazuna-0.3.0/docs/configuration.md +169 -0
  16. tazuna-0.3.0/docs/evals.md +75 -0
  17. tazuna-0.3.0/docs/handoff.md +157 -0
  18. tazuna-0.3.0/docs/packs.md +45 -0
  19. tazuna-0.3.0/docs/quickstart.md +103 -0
  20. tazuna-0.3.0/docs/routing.md +60 -0
  21. tazuna-0.3.0/docs/security.md +40 -0
  22. tazuna-0.3.0/evals/suites/smoke/README.md +11 -0
  23. tazuna-0.3.0/evals/suites/smoke/handoff-contract/case.toml +2 -0
  24. tazuna-0.3.0/evals/suites/smoke/handoff-contract/check.sh +10 -0
  25. tazuna-0.3.0/evals/suites/smoke/handoff-contract/task.md +1 -0
  26. tazuna-0.3.0/evals/suites/smoke/read-only-run/case.toml +3 -0
  27. tazuna-0.3.0/evals/suites/smoke/read-only-run/check.sh +7 -0
  28. tazuna-0.3.0/evals/suites/smoke/read-only-run/seed/notes.txt +2 -0
  29. tazuna-0.3.0/evals/suites/smoke/read-only-run/task.md +1 -0
  30. tazuna-0.3.0/evals/suites/smoke/seeded-repo/case.toml +2 -0
  31. tazuna-0.3.0/evals/suites/smoke/seeded-repo/seed/src/app.py +2 -0
  32. tazuna-0.3.0/evals/suites/smoke/seeded-repo/task.md +1 -0
  33. tazuna-0.3.0/packs/example-pack/README.md +24 -0
  34. tazuna-0.3.0/packs/example-pack/appendix/claude.md +4 -0
  35. tazuna-0.3.0/packs/example-pack/bin/echo-agent.sh +18 -0
  36. tazuna-0.3.0/packs/example-pack/pack.toml +23 -0
  37. tazuna-0.3.0/pyproject.toml +50 -0
  38. tazuna-0.3.0/src/tazuna/__init__.py +4 -0
  39. tazuna-0.3.0/src/tazuna/__main__.py +3 -0
  40. tazuna-0.3.0/src/tazuna/cli.py +507 -0
  41. tazuna-0.3.0/src/tazuna/config.py +354 -0
  42. tazuna-0.3.0/src/tazuna/doctor.py +236 -0
  43. tazuna-0.3.0/src/tazuna/errors.py +43 -0
  44. tazuna-0.3.0/src/tazuna/evals.py +348 -0
  45. tazuna-0.3.0/src/tazuna/gitstate.py +348 -0
  46. tazuna-0.3.0/src/tazuna/guard.py +83 -0
  47. tazuna-0.3.0/src/tazuna/handoff.py +313 -0
  48. tazuna-0.3.0/src/tazuna/providers/claude.toml +16 -0
  49. tazuna-0.3.0/src/tazuna/providers/codex.toml +15 -0
  50. tazuna-0.3.0/src/tazuna/providers/cursor.toml +12 -0
  51. tazuna-0.3.0/src/tazuna/providers/gemini.toml +16 -0
  52. tazuna-0.3.0/src/tazuna/providers/mock.toml +7 -0
  53. tazuna-0.3.0/src/tazuna/providers/ollama.toml +11 -0
  54. tazuna-0.3.0/src/tazuna/render.py +160 -0
  55. tazuna-0.3.0/src/tazuna/routing.py +175 -0
  56. tazuna-0.3.0/src/tazuna/run.py +659 -0
  57. tazuna-0.3.0/src/tazuna/targets.toml +46 -0
  58. tazuna-0.3.0/src/tazuna/templates/PROJECT.md +47 -0
  59. tazuna-0.3.0/src/tazuna/templates/empty-mcp.json +1 -0
  60. tazuna-0.3.0/src/tazuna/templates/tazuna.toml +39 -0
  61. tazuna-0.3.0/tazuna.toml +37 -0
  62. tazuna-0.3.0/tests/conftest.py +130 -0
  63. tazuna-0.3.0/tests/test_config.py +103 -0
  64. tazuna-0.3.0/tests/test_doctor_guard_evals_cli.py +473 -0
  65. tazuna-0.3.0/tests/test_render.py +118 -0
  66. tazuna-0.3.0/tests/test_run.py +927 -0
  67. tazuna-0.3.0/uv.lock +107 -0
@@ -0,0 +1,61 @@
1
+ ---
2
+ description: Project contract (generated from PROJECT.md)
3
+ alwaysApply: true
4
+ ---
5
+ <!-- GENERATED-FROM: PROJECT.md by tazuna — do not edit. Edit PROJECT.md and run `tazuna render`. -->
6
+
7
+ # tazuna — PROJECT.md
8
+
9
+ <!-- Single source of project policy. CLAUDE.md, AGENTS.md and .cursor/rules/project.mdc are
10
+ GENERATED from this file by `tazuna render`. Edit here, then re-render. -->
11
+
12
+ ## Purpose and current state
13
+
14
+ tazuna is a provider-neutral control plane for AI coding agents: one policy file rendered into every harness, static role-based routing across provider CLIs, a uniform HANDOFF + exit-code result contract, reproducible evals, and diagnostics. This repository dogfoods itself: the files you are reading were rendered by `tazuna render`. Version 0.1.0, alpha.
15
+
16
+ ## Stack
17
+
18
+ | Item | Value |
19
+ |---|---|
20
+ | Language | Python 3.11+, standard library only at runtime (tomllib, subprocess, argparse) |
21
+ | Layout | `src/tazuna/` package (`cli.py`, `config.py`, `render.py`, `routing.py`, `run.py`, `handoff.py`, `evals.py`, `doctor.py`, `guard.py`), package data in `providers/*.toml`, `targets.toml`, `templates/` |
22
+ | Tests | pytest in `tests/`; shims only, never a real provider |
23
+ | Docs | `docs/*.md`; `README.md` is the front page |
24
+ | Extension | `packs/` (see `docs/packs.md`) |
25
+ | Evals | `evals/suites/smoke` (offline) |
26
+
27
+ ## Commands
28
+
29
+ | Purpose | Command |
30
+ |---|---|
31
+ | Install for development | `uv venv && uv pip install -e '.[dev]'` |
32
+ | Test | `.venv/bin/pytest` |
33
+ | Lint | `.venv/bin/ruff check src tests` |
34
+ | Self-check the contract layer | `tazuna render --check && tazuna doctor` |
35
+ | Offline eval | `tazuna eval run evals/suites/smoke --role dry` |
36
+
37
+ ## Quality gate (required before merge)
38
+
39
+ - [ ] `pytest` passes on Linux and macOS (CI matrix)
40
+ - [ ] `ruff check src tests` is clean
41
+ - [ ] `tazuna render --check` and `tazuna doctor` pass in this repository
42
+ - [ ] User-visible changes are in `CHANGELOG.md` and the relevant `docs/*.md`
43
+ - [ ] A provider definition change comes with an argv test against a shim
44
+
45
+ ## Do not
46
+
47
+ - Add runtime dependencies. `tomllib` and the standard library are enough.
48
+ - Read, write, or log `.env`, `.env.*`, `secrets/**`, or credential files.
49
+ - Edit generated files (`CLAUDE.md`, `AGENTS.md`, `.cursor/rules/project.mdc`); edit `PROJECT.md` and run `tazuna render`.
50
+ - Add flags to provider definitions that disable a harness's approvals or sandbox (`--dangerously-*`, `--yolo`, `--force`); `run.py` rejects them.
51
+ - Let tests reach a real provider CLI; use the shims in `tests/conftest.py`.
52
+
53
+ ## Known pitfalls
54
+
55
+ - `is_generated` looks at the first eight lines; a target whose frontmatter is longer will be treated as hand-written. Keep `frontmatter` short.
56
+ - Exit codes are the automation contract. The provider's self-report refines `status` only; never let it change the exit code.
57
+ - Provider CLIs change flags between releases. Verify with `tazuna run --role X --dry-run` after upgrading a CLI, and keep `experimental = true` on definitions nobody has run end to end.
58
+
59
+ ## References
60
+
61
+ - Architecture: `docs/architecture.md` · Security model: `docs/security.md` · Result contract: `docs/handoff.md`
@@ -0,0 +1,28 @@
1
+ name: ci
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ strategy:
11
+ fail-fast: false
12
+ matrix:
13
+ os: [ubuntu-latest, macos-latest]
14
+ python: ["3.11", "3.12", "3.13"]
15
+ runs-on: ${{ matrix.os }}
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+ - uses: actions/setup-python@v5
19
+ with:
20
+ python-version: ${{ matrix.python }}
21
+ - run: python -m pip install -e '.[dev]'
22
+ - run: ruff check src tests
23
+ - run: pytest
24
+ - name: contract self-check
25
+ run: |
26
+ tazuna render --check
27
+ tazuna doctor
28
+ tazuna eval run evals/suites/smoke --role dry
@@ -0,0 +1,54 @@
1
+ name: release
2
+
3
+ # Builds the sdist and wheel for every published GitHub release and uploads them to PyPI
4
+ # through trusted publishing (no API token stored in the repository).
5
+ #
6
+ # One-time setup by the repository owner, after which the `publish` job runs automatically:
7
+ # 1. On PyPI: add a pending GitHub publisher for project "tazuna" (a new name, not a rename of
8
+ # "agentplane", so the publisher set up for agentplane does not carry over)
9
+ # (owner KK-18-dot, repository tazuna, workflow release.yml, environment pypi).
10
+ # 2. On GitHub: Settings → Environments → create "pypi".
11
+ # 3. On GitHub: Settings → Variables → Actions → PYPI_TRUSTED_PUBLISHER = true.
12
+ # Until step 3 is done the publish job is skipped, so a release never fails on a missing publisher.
13
+
14
+ on:
15
+ release:
16
+ types: [published]
17
+ workflow_dispatch:
18
+
19
+ jobs:
20
+ build:
21
+ runs-on: ubuntu-latest
22
+ steps:
23
+ - uses: actions/checkout@v4
24
+ - uses: actions/setup-python@v5
25
+ with:
26
+ python-version: "3.12"
27
+ - run: python -m pip install build
28
+ - run: python -m build
29
+ - name: smoke-test the wheel in a clean environment
30
+ run: |
31
+ python -m venv /tmp/v
32
+ /tmp/v/bin/pip install -q dist/*.whl
33
+ /tmp/v/bin/tazuna --version
34
+ /tmp/v/bin/tazuna init --dir /tmp/p
35
+ /tmp/v/bin/tazuna render --dir /tmp/p
36
+ /tmp/v/bin/tazuna run --role dry --dir /tmp/p --quiet "release smoke"
37
+ - uses: actions/upload-artifact@v4
38
+ with:
39
+ name: dist
40
+ path: dist/
41
+
42
+ publish:
43
+ needs: build
44
+ if: vars.PYPI_TRUSTED_PUBLISHER == 'true'
45
+ runs-on: ubuntu-latest
46
+ environment: pypi
47
+ permissions:
48
+ id-token: write
49
+ steps:
50
+ - uses: actions/download-artifact@v4
51
+ with:
52
+ name: dist
53
+ path: dist/
54
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,18 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .venv/
4
+ venv/
5
+ dist/
6
+ build/
7
+ *.egg-info/
8
+ .pytest_cache/
9
+ .ruff_cache/
10
+ # local secrets and environment files must never be committed
11
+ .env
12
+ .env.*
13
+ !.env.example
14
+ secrets/
15
+ # eval results are local artifacts unless deliberately committed as baselines
16
+ evals/results/
17
+ # the owner's local work ledger (dev-platform backlog.sh / board read it from disk)
18
+ BACKLOG.md
tazuna-0.3.0/AGENTS.md ADDED
@@ -0,0 +1,57 @@
1
+ <!-- GENERATED-FROM: PROJECT.md by tazuna — do not edit. Edit PROJECT.md and run `tazuna render`. -->
2
+
3
+ # tazuna — PROJECT.md
4
+
5
+ <!-- Single source of project policy. CLAUDE.md, AGENTS.md and .cursor/rules/project.mdc are
6
+ GENERATED from this file by `tazuna render`. Edit here, then re-render. -->
7
+
8
+ ## Purpose and current state
9
+
10
+ tazuna is a provider-neutral control plane for AI coding agents: one policy file rendered into every harness, static role-based routing across provider CLIs, a uniform HANDOFF + exit-code result contract, reproducible evals, and diagnostics. This repository dogfoods itself: the files you are reading were rendered by `tazuna render`. Version 0.1.0, alpha.
11
+
12
+ ## Stack
13
+
14
+ | Item | Value |
15
+ |---|---|
16
+ | Language | Python 3.11+, standard library only at runtime (tomllib, subprocess, argparse) |
17
+ | Layout | `src/tazuna/` package (`cli.py`, `config.py`, `render.py`, `routing.py`, `run.py`, `handoff.py`, `evals.py`, `doctor.py`, `guard.py`), package data in `providers/*.toml`, `targets.toml`, `templates/` |
18
+ | Tests | pytest in `tests/`; shims only, never a real provider |
19
+ | Docs | `docs/*.md`; `README.md` is the front page |
20
+ | Extension | `packs/` (see `docs/packs.md`) |
21
+ | Evals | `evals/suites/smoke` (offline) |
22
+
23
+ ## Commands
24
+
25
+ | Purpose | Command |
26
+ |---|---|
27
+ | Install for development | `uv venv && uv pip install -e '.[dev]'` |
28
+ | Test | `.venv/bin/pytest` |
29
+ | Lint | `.venv/bin/ruff check src tests` |
30
+ | Self-check the contract layer | `tazuna render --check && tazuna doctor` |
31
+ | Offline eval | `tazuna eval run evals/suites/smoke --role dry` |
32
+
33
+ ## Quality gate (required before merge)
34
+
35
+ - [ ] `pytest` passes on Linux and macOS (CI matrix)
36
+ - [ ] `ruff check src tests` is clean
37
+ - [ ] `tazuna render --check` and `tazuna doctor` pass in this repository
38
+ - [ ] User-visible changes are in `CHANGELOG.md` and the relevant `docs/*.md`
39
+ - [ ] A provider definition change comes with an argv test against a shim
40
+
41
+ ## Do not
42
+
43
+ - Add runtime dependencies. `tomllib` and the standard library are enough.
44
+ - Read, write, or log `.env`, `.env.*`, `secrets/**`, or credential files.
45
+ - Edit generated files (`CLAUDE.md`, `AGENTS.md`, `.cursor/rules/project.mdc`); edit `PROJECT.md` and run `tazuna render`.
46
+ - Add flags to provider definitions that disable a harness's approvals or sandbox (`--dangerously-*`, `--yolo`, `--force`); `run.py` rejects them.
47
+ - Let tests reach a real provider CLI; use the shims in `tests/conftest.py`.
48
+
49
+ ## Known pitfalls
50
+
51
+ - `is_generated` looks at the first eight lines; a target whose frontmatter is longer will be treated as hand-written. Keep `frontmatter` short.
52
+ - Exit codes are the automation contract. The provider's self-report refines `status` only; never let it change the exit code.
53
+ - Provider CLIs change flags between releases. Verify with `tazuna run --role X --dry-run` after upgrading a CLI, and keep `experimental = true` on definitions nobody has run end to end.
54
+
55
+ ## References
56
+
57
+ - Architecture: `docs/architecture.md` · Security model: `docs/security.md` · Result contract: `docs/handoff.md`
@@ -0,0 +1,82 @@
1
+ # Changelog
2
+
3
+ ## 0.3.0 — 2026-09-26
4
+
5
+ ### Upgrade notes
6
+
7
+ - The project is renamed from `agentplane` to `tazuna` (手綱, "the reins"). There is no compatibility layer: the package, the command, the module, the config file, the environment variables and the self-report line all change, and the old names are not read. To move a project:
8
+ - `pipx uninstall agentplane && pipx install tazuna` (or the same with pip).
9
+ - Rename `agentplane.toml` to `tazuna.toml`, a `.agentplane/` directory to `.tazuna/`, and `~/.config/agentplane/` to `~/.config/tazuna/`. `tazuna doctor` warns while any of these is left, and a missing `tazuna.toml` next to an `agentplane.toml` is reported as a rename, not as "run init".
10
+ - Replace `agentplane` with `tazuna` in CI, Makefiles, scripts and hook commands (`tazuna guard --hook claude`), and `AGENTPLANE_` with `TAZUNA_` in scripts and eval `check.sh` files.
11
+ - Replace `AGENTPLANE-STATUS:` with `TAZUNA-STATUS:` wherever it is written or matched: policy text that tells agents how to report, pack provider scripts, `[providers.mock] response`, and eval `expect_output_regex`. An old line is not recognized, so a run that reported BLOCKED under the old name is recorded as `done`.
12
+ - Run `tazuna render` and commit the result. The generated header now names tazuna, so `render --check` reports drift until you do; the files themselves are still recognized as generated, because the marker does not carry the tool name.
13
+ - The run ledger moves to `$XDG_STATE_HOME/tazuna`; the old ledger stays where it was.
14
+
15
+ ### Fixed
16
+
17
+ - `agentplane guard --hook claude` and `agentplane guard --print-hook` failed with "unrecognized arguments" and exit 2. The hook command that `--print-hook` and the `guard` docstring describe would therefore have made Claude Code block every Edit / Write / MultiEdit call. `--hook` and `--print-hook` are now real options, the paths form is unchanged, and tests call `guard` through the CLI.
18
+ - An unknown `--hook` value no longer exits 2 (which a harness reads as a block); guard warns on stderr and allows, as the rest of the hook does.
19
+
20
+ ### Added
21
+
22
+ - The quickstart shows how to install the Claude Code hook with `tazuna guard --print-hook`.
23
+
24
+ ## 0.2.0 — 2026-09-17
25
+
26
+ ### Upgrade notes
27
+
28
+ - The forbidden-flag check is stricter (see Fixed). A provider table that passed `-f`, `-y`, `--force=…` or a `bypassPermissions` / `danger-full-access` value is now refused with exit 3, and `doctor` names it.
29
+ - New exit codes 130 / 143 and status `cancelled`. Scripts that treated any non-zero exit as a provider failure keep working; scripts that match statuses should add `cancelled`.
30
+ - The ledger gains `parent`; `command` holds `<task>` for argument-passing providers. Existing fields keep their meaning.
31
+
32
+ ### Fixed
33
+
34
+ - `changed` no longer misses real changes. The before/after snapshot now records `HEAD` and a content hash per dirty path (untracked directories expanded), so a second edit to a file that was already modified, a new file inside an already-untracked directory, and commits made by the provider (`commits: a..b (N)` plus `committed <S> <path>` lines) all show up. Paths that were dirty before and are clean now are listed too. Entries stay plain strings in the ledger.
35
+ - Run ids carry a random suffix and log files are created exclusively (`O_EXCL`), so eval trials started by one process within the same second no longer share, and overwrite, one log.
36
+ - A fallback no longer runs a `read_only = true` fallback role writable when the original route was writable. Fallback can only tighten read-only.
37
+ - Cancelling agentplane no longer leaves the provider running. SIGINT / SIGTERM / SIGHUP stop the provider's process group, and the run is still recorded (HANDOFF + ledger) with the new status `cancelled` and exit code 130 (SIGINT) or 143 (SIGTERM, SIGHUP). Cancelled runs never trigger the fallback. The task is now fed to the provider's stdin from a thread, so a provider that does not read a large task can no longer block the timeout.
38
+ - The forbidden-flag check matched exact strings only. It now also refuses `--dangerously-*=…`, `--force=…`, the short forms `-f` / `-y`, and the values `bypassPermissions`, `danger-full-access` and `yolo` as separate arguments or after `=` (covering `--permission-mode bypassPermissions`, `--sandbox=danger-full-access`, `-c sandbox_mode=danger-full-access`, `--approval-mode yolo`). Task text is no longer checked, so a task reading `--force` is not refused. `doctor` warns about such flags in any provider table.
39
+ - The ledger no longer stores the full task for `task_via = "arg"` providers (cursor, gemini): `command` records `<task>` in its place, and `task_head` is masked like the HANDOFF. Ledger lines are appended with a single `os.write` on an `O_APPEND` descriptor.
40
+ - State files were world-readable. The state directory, `logs/` and `evals/` are now created 0700, log files and `runs.jsonl` 0600, without touching the umask the provider inherits. `doctor` warns when an existing state directory or ledger is open to group/other and prints the `chmod` fix. Token shapes are masked in the log on disk after each run.
41
+ - `eval report` now implements the documented rule: runs with status `quota-exhausted`, `auth-required` or `cancelled` are excluded from pass rates and counted in a new `excluded` column. `timeout` still counts as a failure, and `docs/evals.md` now says so.
42
+ - Ctrl-C during `eval run` stopped only the current trial and the suite went on, with the cancelled trial counted as a failure. A cancelled trial now stops the suite (exit 130 / 143) after its row and the report are written, even when its HANDOFF could not be written.
43
+ - A task that is not valid UTF-8 crashed after the provider had started and left no ledger row (and, for stdin providers, a provider waiting until the timeout). It is now refused before launch with exit 2 (so is a task containing a NUL byte, which crashed arg-passing providers), whether it came as an argument, on stdin or through `--task-file` (which printed a traceback). Any error while writing the HANDOFF now ends as `handoff-write-failed` with a ledger row, and the provider's stdin is always closed.
44
+ - A provider's short final lines, including its `AGENTPLANE-STATUS` line, could be lost when a process it started detached and kept the output pipe open. Output is now logged as soon as it is readable.
45
+ - A signal that arrived while the HANDOFF and ledger row were being written could kill agentplane between the two. Both are now written first. When a signal stops a run before its fallback, the HANDOFF describes that run instead of keeping an older file.
46
+
47
+ ### Security
48
+
49
+ Findings from a review of the changes above, fixed before release:
50
+
51
+ - The git commands behind `changed`, `doctor` and `status` no longer run commands a provider planted in the repository (`core.fsmonitor`, filter drivers, the post-index-change hook), and they get the allowlisted environment instead of agentplane's own. A filter driver whose name cannot be overridden makes the snapshot refuse, with a note in `changed`.
52
+ - File names in `changed` are quoted with git-style escapes when they contain control or format characters, quotes or backslashes, so a file name can no longer add sections to the HANDOFF. A trailing CR in a name is hashed correctly.
53
+ - `changed` also lists newly set `skip-worktree` / `assume-unchanged` flags and changes to git's `config`, `info/exclude`, `info/attributes` and `hooks/*`.
54
+ - With a nested `--out` (`--out dir/a/b/HANDOFF.md`), a provider that replaced `dir/a` with a symlink could make agentplane write the HANDOFF outside `--dir`. The output directory is now opened one component at a time from `--dir` without following symlinks (present since 0.1.0).
55
+ - A failing or slow snapshot (for example a huge file) no longer loses the run: files above 8 MiB are fingerprinted by size and mtime, FIFOs and devices are never read, and any snapshot error becomes a note in `changed`.
56
+ - A signal that arrives while output is still being collected cancels the run (no fallback). Processes the provider left holding its output are killed with its process group when the run is over, and later output is dropped instead of reaching the log unmasked.
57
+ - The forbidden-flag check also splits at every `=` (`--config=sandbox_mode=danger-full-access`), ignores surrounding whitespace, refuses any flag or config key containing `dangerously` (`--allow-dangerously-skip-permissions`), and refuses `bypassPermissions` / `danger-full-access` anywhere in an element.
58
+ - The empty MCP config handed to Claude Code is re-checked before each run, model ids may no longer start with `-`, `doctor` warns when the state directory belongs to another user, `check.sh` failure reasons are masked, and `eval report --fail-on-regression` names the baseline cases it could not compare.
59
+
60
+ ### Added
61
+
62
+ - Run lineage: the ledger has a `parent` field, taken from `AGENTPLANE_PARENT` when it is a plain token (otherwise ignored with a warning). agentplane sets `AGENTPLANE_PARENT` to the current run id for every provider it launches, so nested delegation is traceable from the ledger, and scripts or CI can set it to their own id.
63
+ - `agentplane run --json` prints the final ledger record as one JSON object on stdout (no provider echo, no `HANDOFF:` line).
64
+ - `docs/handoff.md` documents the ledger fields as a stable interface and the `status` values as a closed vocabulary.
65
+ - `agentplane eval report RESULTS --baseline BASE --fail-on-regression` exits 1 when a case present in both files has a lower pass rate than the baseline, or when the results are incomplete (a cancelled or excluded trial, or a missing baseline case), for CI. A subset run (`--only`) against a full baseline, or a run during a provider outage, therefore fails the gate.
66
+ - Eval result rows carry `run_id`, the ledger id of the run.
67
+ - `max_bytes` on render targets. The built-in `codex` target sets 32768, the size at which Codex CLI stops reading `AGENTS.md`. An oversized target is still written with a warning, fails `render --check` as `too-large`, and is a `doctor` WARN.
68
+
69
+ ### Internal
70
+
71
+ - The git snapshot and the hardened git helper moved from `agentplane.run` to `agentplane.gitstate`. The Python modules are not a stable interface; the CLI, HANDOFF and ledger are.
72
+
73
+ ## 0.1.0 — 2026-09-13
74
+
75
+ First public release, available on PyPI (`pipx install agentplane`).
76
+
77
+ - `init`, `render` (`--check`, `--adopt`, `--force`, appendices, `{{model:…}}` expansion), built-in targets for Claude Code, Codex/AGENTS.md, Cursor, Gemini CLI, Copilot, Windsurf, Cline.
78
+ - `run` with role-based routing, explicit overrides, allowlisted environment, timeout, safety boundaries, typed self-report, failure classification, one-shot fallback, HANDOFF.md, JSONL ledger.
79
+ - Built-in providers: claude, codex, cursor, gemini (experimental), ollama, mock.
80
+ - `routes`, `doctor` (OK/WARN/NOTE), `status`, `runs`, `packs`, `guard` (Claude Code hook adapter).
81
+ - `eval run` / `eval report` with fixture suites, code graders, signed baseline diffs; bundled offline smoke suite.
82
+ - Packs: directories of the same TOML tables, with `{pack_dir}` and pack-relative appendices.
tazuna-0.3.0/CLAUDE.md ADDED
@@ -0,0 +1,64 @@
1
+ <!-- GENERATED-FROM: PROJECT.md by tazuna — do not edit. Edit PROJECT.md and run `tazuna render`. -->
2
+
3
+ # tazuna — PROJECT.md
4
+
5
+ <!-- Single source of project policy. CLAUDE.md, AGENTS.md and .cursor/rules/project.mdc are
6
+ GENERATED from this file by `tazuna render`. Edit here, then re-render. -->
7
+
8
+ ## Purpose and current state
9
+
10
+ tazuna is a provider-neutral control plane for AI coding agents: one policy file rendered into every harness, static role-based routing across provider CLIs, a uniform HANDOFF + exit-code result contract, reproducible evals, and diagnostics. This repository dogfoods itself: the files you are reading were rendered by `tazuna render`. Version 0.1.0, alpha.
11
+
12
+ ## Stack
13
+
14
+ | Item | Value |
15
+ |---|---|
16
+ | Language | Python 3.11+, standard library only at runtime (tomllib, subprocess, argparse) |
17
+ | Layout | `src/tazuna/` package (`cli.py`, `config.py`, `render.py`, `routing.py`, `run.py`, `handoff.py`, `evals.py`, `doctor.py`, `guard.py`), package data in `providers/*.toml`, `targets.toml`, `templates/` |
18
+ | Tests | pytest in `tests/`; shims only, never a real provider |
19
+ | Docs | `docs/*.md`; `README.md` is the front page |
20
+ | Extension | `packs/` (see `docs/packs.md`) |
21
+ | Evals | `evals/suites/smoke` (offline) |
22
+
23
+ ## Commands
24
+
25
+ | Purpose | Command |
26
+ |---|---|
27
+ | Install for development | `uv venv && uv pip install -e '.[dev]'` |
28
+ | Test | `.venv/bin/pytest` |
29
+ | Lint | `.venv/bin/ruff check src tests` |
30
+ | Self-check the contract layer | `tazuna render --check && tazuna doctor` |
31
+ | Offline eval | `tazuna eval run evals/suites/smoke --role dry` |
32
+
33
+ ## Quality gate (required before merge)
34
+
35
+ - [ ] `pytest` passes on Linux and macOS (CI matrix)
36
+ - [ ] `ruff check src tests` is clean
37
+ - [ ] `tazuna render --check` and `tazuna doctor` pass in this repository
38
+ - [ ] User-visible changes are in `CHANGELOG.md` and the relevant `docs/*.md`
39
+ - [ ] A provider definition change comes with an argv test against a shim
40
+
41
+ ## Do not
42
+
43
+ - Add runtime dependencies. `tomllib` and the standard library are enough.
44
+ - Read, write, or log `.env`, `.env.*`, `secrets/**`, or credential files.
45
+ - Edit generated files (`CLAUDE.md`, `AGENTS.md`, `.cursor/rules/project.mdc`); edit `PROJECT.md` and run `tazuna render`.
46
+ - Add flags to provider definitions that disable a harness's approvals or sandbox (`--dangerously-*`, `--yolo`, `--force`); `run.py` rejects them.
47
+ - Let tests reach a real provider CLI; use the shims in `tests/conftest.py`.
48
+
49
+ ## Known pitfalls
50
+
51
+ - `is_generated` looks at the first eight lines; a target whose frontmatter is longer will be treated as hand-written. Keep `frontmatter` short.
52
+ - Exit codes are the automation contract. The provider's self-report refines `status` only; never let it change the exit code.
53
+ - Provider CLIs change flags between releases. Verify with `tazuna run --role X --dry-run` after upgrading a CLI, and keep `experimental = true` on definitions nobody has run end to end.
54
+
55
+ ## References
56
+
57
+ - Architecture: `docs/architecture.md` · Security model: `docs/security.md` · Result contract: `docs/handoff.md`
58
+
59
+ <!-- appendix: claude.md -->
60
+
61
+ ## Claude Code specifics (from example-pack)
62
+
63
+ - This section is appended to `CLAUDE.md` only; other targets do not see it.
64
+ - Put harness-specific instructions here (tool preferences, slash commands, hook expectations) and keep `PROJECT.md` harness-neutral.
@@ -0,0 +1,38 @@
1
+ # Contributing
2
+
3
+ ## Setup
4
+
5
+ ```bash
6
+ git clone https://github.com/KK-18-dot/tazuna
7
+ cd tazuna
8
+ uv venv && uv pip install -e '.[dev]' # or: python -m venv .venv && .venv/bin/pip install -e '.[dev]'
9
+ .venv/bin/pytest
10
+ .venv/bin/ruff check src tests
11
+ ```
12
+
13
+ Tests never touch a real provider: they run in a throwaway HOME with a PATH that contains only recorded shims. Keep it that way. If you add a provider definition, add a test that asserts the exact argv the shim receives.
14
+
15
+ ## What fits
16
+
17
+ tazuna does four things: render policy, route roles, record results, grade evals. Contributions that make those four smaller, safer, or clearer are welcome. Things that belong elsewhere:
18
+
19
+ - skill libraries, agent personas, workflow engines (put them in a pack, or in your harness)
20
+ - schedulers, daemons, multiplexer integrations
21
+ - anything that requires a runtime dependency
22
+
23
+ ## Provider definitions
24
+
25
+ Built-in providers live in `src/tazuna/providers/*.toml`. When a CLI changes its flags, update the table, note the CLI version in the commit message, and add or adjust the argv test. Mark a provider `experimental = true` until someone has run the smoke suite against it end to end.
26
+
27
+ ## Releasing
28
+
29
+ 1. Bump `version` in `pyproject.toml` and `__version__` in `src/tazuna/__init__.py`; add a `CHANGELOG.md` entry.
30
+ 2. `git tag -a vX.Y.Z -m "vX.Y.Z" && git push --tags`, then create a GitHub release from the tag.
31
+ 3. `.github/workflows/release.yml` builds the sdist and wheel, smoke-tests the wheel in a clean environment, and publishes both to PyPI through trusted publishing with provenance attestations. The `pypi` environment is configured. The trusted publisher was set up for the old name `agentplane` and must be added again for `tazuna` before the first release under the new name (see the comment at the top of `release.yml`). The repository variable `PYPI_TRUSTED_PUBLISHER` (`true`) gates the publish job.
32
+ 4. Check the result: `pipx install --force tazuna==X.Y.Z && tazuna --version` (or the same with `python -m pip` in a fresh virtual environment).
33
+
34
+ ## Commits and pull requests
35
+
36
+ - `type: description` (feat / fix / docs / test / refactor / chore). The body says why, including alternatives you rejected.
37
+ - Run `pytest` and `ruff check` before opening a PR; CI runs both on Linux and macOS.
38
+ - Document user-visible changes in `CHANGELOG.md`.
tazuna-0.3.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 tazuna contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
tazuna-0.3.0/PKG-INFO ADDED
@@ -0,0 +1,141 @@
1
+ Metadata-Version: 2.5
2
+ Name: tazuna
3
+ Version: 0.3.0
4
+ Summary: Define project policy once, render it into every agent harness, route work across providers, and get auditable results.
5
+ Project-URL: Homepage, https://github.com/KK-18-dot/tazuna
6
+ Project-URL: Issues, https://github.com/KK-18-dot/tazuna/issues
7
+ Author: tazuna contributors
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: agents-md,ai-agents,claude-code,codex,cursor,evals,routing
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: POSIX
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Software Development :: Build Tools
21
+ Requires-Python: >=3.11
22
+ Provides-Extra: dev
23
+ Requires-Dist: pytest>=8; extra == 'dev'
24
+ Requires-Dist: ruff>=0.5; extra == 'dev'
25
+ Description-Content-Type: text/markdown
26
+
27
+ # tazuna
28
+
29
+ [![PyPI](https://img.shields.io/pypi/v/tazuna)](https://pypi.org/project/tazuna/) [![Python](https://img.shields.io/pypi/pyversions/tazuna)](https://pypi.org/project/tazuna/)
30
+
31
+ **Define project policy once. Render it into every agent harness. Route work across providers. Get auditable results.**
32
+
33
+ *Tazuna* (手綱) is Japanese for the reins: one pair of hands steering several horses. Versions up to 0.2.0 were published as `agentplane`; see the [changelog](https://github.com/KK-18-dot/tazuna/blob/main/CHANGELOG.md) for the rename.
34
+
35
+ tazuna is a small, provider-neutral control plane for teams that use more than one AI coding agent (Claude Code, Codex, Cursor, Gemini CLI, local models, or anything with a headless CLI). It does four things and nothing else:
36
+
37
+ | | What | Command |
38
+ |---|---|---|
39
+ | 1 | **One policy, many harnesses.** `PROJECT.md` is the single source; `CLAUDE.md`, `AGENTS.md`, `.cursor/rules/project.mdc` (and more) are generated from it and guarded against hand edits. | `tazuna render` |
40
+ | 2 | **Understandable routing.** A *role* is a named row in `tazuna.toml`: provider, model, effort, timeout, read-only, fallback. Nothing is chosen by heuristics at runtime. | `tazuna routes` |
41
+ | 3 | **Auditable execution.** Every delegated task ends in a `HANDOFF.md` built from measured facts (git diff, exit code, duration, effective model), a typed status, and a JSONL ledger. | `tazuna run` |
42
+ | 4 | **Reproducible evals and diagnostics.** Fixture directories run through the same path and are graded deterministically; `doctor` says what is broken vs. merely optional. | `tazuna eval`, `tazuna doctor` |
43
+
44
+ It is *not* a skill library, an agent framework, or a swarm runtime. It sits under those and gives them one policy file, one routing table, and one result contract.
45
+
46
+ ## Install
47
+
48
+ Python 3.11+ and git. No other runtime dependencies.
49
+
50
+ tazuna is published on [PyPI](https://pypi.org/project/tazuna/). Install the CLI into its own environment with pipx:
51
+
52
+ ```bash
53
+ pipx install tazuna
54
+ tazuna --version
55
+ ```
56
+
57
+ If you do not use pipx, pip works too (inside a virtual environment):
58
+
59
+ ```bash
60
+ python -m pip install tazuna
61
+ ```
62
+
63
+ `uv tool install tazuna` is equivalent to the pipx command. Release artifacts on PyPI are uploaded from GitHub Actions through trusted publishing and carry provenance attestations. To work on tazuna itself, install from a checkout as described in [CONTRIBUTING.md](CONTRIBUTING.md).
64
+
65
+ Provider CLIs are optional. Install whichever you use (`claude`, `codex`, `cursor-agent`, `gemini`, `ollama`); tazuna detects them and works offline with a built-in mock provider when none is present.
66
+
67
+ ### Use it with whatever plan you have
68
+
69
+ tazuna never talks to a model API itself. It launches the provider CLIs you already have, under the login and billing you already use, so pick the setup that matches your contract:
70
+
71
+ | You have | Use | Notes |
72
+ |---|---|---|
73
+ | A subscription that includes a CLI (Claude Pro/Max → `claude`, ChatGPT Plus/Pro → `codex`, Cursor → `cursor-agent`, Google AI → `gemini`) | roles on that provider | the CLI's own login is used; no API key is needed or forwarded |
74
+ | API keys instead of a subscription | the same CLIs configured for API billing, per each vendor's docs | keys stay in the CLI's own config; tazuna's environment allowlist does not forward them |
75
+ | Several of the above | one role per provider, `fallback` between them | `routes` shows which provider each role bills |
76
+ | No paid plan, or private data | `ollama` with a local model, `read_only` | offline, nothing leaves the machine |
77
+ | Nothing yet | the `mock` provider (`--role dry`) | exercises the whole pipeline without a model |
78
+
79
+ The model ids written by `tazuna init` are examples. Replace them with the ids your plan actually enables (each CLI can list its models), keep personal choices in `~/.config/tazuna/config.toml`, and check the resolved command with `tazuna run --role X --dry-run` before spending quota.
80
+
81
+ ## Quickstart (5 minutes)
82
+
83
+ ```bash
84
+ cd your-project
85
+ tazuna init # writes tazuna.toml + PROJECT.md, roles for the CLIs it finds
86
+ $EDITOR PROJECT.md # describe the project once: purpose, stack, commands, quality gate, do-nots
87
+ tazuna render # -> CLAUDE.md, AGENTS.md, .cursor/rules/project.mdc
88
+ tazuna routes # what each role resolves to, and whether its provider is installed
89
+ tazuna doctor # OK / WARN / NOTE; exit 1 only on real problems
90
+ ```
91
+
92
+ Delegate a task and read the result:
93
+
94
+ ```bash
95
+ tazuna run --role dry "Summarize the repo layout in five bullets" # offline mock, always works
96
+ tazuna run --role review --read-only "Review src/ for missing error handling"
97
+ tazuna run --role impl --task-file PLAN.md --timeout 1200
98
+ cat HANDOFF.md # status, changed files, verification facts, provider output tail
99
+ tazuna runs # ledger of every run
100
+ ```
101
+
102
+ Keep generated files honest in CI:
103
+
104
+ ```bash
105
+ tazuna render --check # exit 1 on drift
106
+ tazuna doctor # exit 1 on WARN
107
+ ```
108
+
109
+ Run the bundled offline eval suite:
110
+
111
+ ```bash
112
+ tazuna eval run evals/suites/smoke --role dry
113
+ ```
114
+
115
+ ## How it fits together
116
+
117
+ ```
118
+ PROJECT.md ──render──▶ CLAUDE.md / AGENTS.md / .cursor/rules/project.mdc / GEMINI.md / …
119
+ ▲ (generated marker; `guard` and `render --check` protect them)
120
+ │
121
+ tazuna.toml ── roles ──▶ run --role X ──▶ provider CLI (env allowlist, timeout, sandbox flags)
122
+ │ │
123
+ └── providers (built-in + packs + user) ▼
124
+ HANDOFF.md + runs.jsonl + log ←── evals grade these
125
+ ```
126
+
127
+ - **Policy layer**: `PROJECT.md` (+ optional per-target appendices in `.tazuna/appendix/`). Model aliases `{{model:NAME}}` expand from `[models]`.
128
+ - **Routing layer**: `[roles.*]` in `tazuna.toml`; personal provider choices go in `~/.config/tazuna/config.toml` and never into the repo.
129
+ - **Execution layer**: `run` launches the provider headless with an allowlisted environment, a timeout, and the provider's own sandbox flags; it never passes flags that disable a harness's approvals.
130
+ - **Result contract**: exit code `0 done · 1 failed · 2 usage · 3 safety boundary · 4 empty output · 124 timeout · 130/143 cancelled`, plus the provider's typed self-report (`DONE`, `DONE_WITH_CONCERNS`, `BLOCKED`, `NEEDS_CONTEXT`) read only from its last line.
131
+ - **Extension**: packs (`pack.toml`) add providers, targets, roles, and appendices. See `packs/`.
132
+
133
+ Full docs: [docs/quickstart.md](docs/quickstart.md) · [docs/configuration.md](docs/configuration.md) · [docs/routing.md](docs/routing.md) · [docs/handoff.md](docs/handoff.md) · [docs/evals.md](docs/evals.md) · [docs/packs.md](docs/packs.md) · [docs/architecture.md](docs/architecture.md) · [docs/security.md](docs/security.md)
134
+
135
+ ## Status
136
+
137
+ 0.3.0, alpha. The Claude Code, Codex, and Cursor provider definitions mirror flags used in production; Gemini CLI is marked experimental. Provider CLIs change their flags; if one breaks, override the provider table in your user config and open an issue.
138
+
139
+ ## License
140
+
141
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,55 @@
1
+ # tazuna — PROJECT.md
2
+
3
+ <!-- Single source of project policy. CLAUDE.md, AGENTS.md and .cursor/rules/project.mdc are
4
+ GENERATED from this file by `tazuna render`. Edit here, then re-render. -->
5
+
6
+ ## Purpose and current state
7
+
8
+ tazuna is a provider-neutral control plane for AI coding agents: one policy file rendered into every harness, static role-based routing across provider CLIs, a uniform HANDOFF + exit-code result contract, reproducible evals, and diagnostics. This repository dogfoods itself: the files you are reading were rendered by `tazuna render`. Version 0.1.0, alpha.
9
+
10
+ ## Stack
11
+
12
+ | Item | Value |
13
+ |---|---|
14
+ | Language | Python 3.11+, standard library only at runtime (tomllib, subprocess, argparse) |
15
+ | Layout | `src/tazuna/` package (`cli.py`, `config.py`, `render.py`, `routing.py`, `run.py`, `handoff.py`, `evals.py`, `doctor.py`, `guard.py`), package data in `providers/*.toml`, `targets.toml`, `templates/` |
16
+ | Tests | pytest in `tests/`; shims only, never a real provider |
17
+ | Docs | `docs/*.md`; `README.md` is the front page |
18
+ | Extension | `packs/` (see `docs/packs.md`) |
19
+ | Evals | `evals/suites/smoke` (offline) |
20
+
21
+ ## Commands
22
+
23
+ | Purpose | Command |
24
+ |---|---|
25
+ | Install for development | `uv venv && uv pip install -e '.[dev]'` |
26
+ | Test | `.venv/bin/pytest` |
27
+ | Lint | `.venv/bin/ruff check src tests` |
28
+ | Self-check the contract layer | `tazuna render --check && tazuna doctor` |
29
+ | Offline eval | `tazuna eval run evals/suites/smoke --role dry` |
30
+
31
+ ## Quality gate (required before merge)
32
+
33
+ - [ ] `pytest` passes on Linux and macOS (CI matrix)
34
+ - [ ] `ruff check src tests` is clean
35
+ - [ ] `tazuna render --check` and `tazuna doctor` pass in this repository
36
+ - [ ] User-visible changes are in `CHANGELOG.md` and the relevant `docs/*.md`
37
+ - [ ] A provider definition change comes with an argv test against a shim
38
+
39
+ ## Do not
40
+
41
+ - Add runtime dependencies. `tomllib` and the standard library are enough.
42
+ - Read, write, or log `.env`, `.env.*`, `secrets/**`, or credential files.
43
+ - Edit generated files (`CLAUDE.md`, `AGENTS.md`, `.cursor/rules/project.mdc`); edit `PROJECT.md` and run `tazuna render`.
44
+ - Add flags to provider definitions that disable a harness's approvals or sandbox (`--dangerously-*`, `--yolo`, `--force`); `run.py` rejects them.
45
+ - Let tests reach a real provider CLI; use the shims in `tests/conftest.py`.
46
+
47
+ ## Known pitfalls
48
+
49
+ - `is_generated` looks at the first eight lines; a target whose frontmatter is longer will be treated as hand-written. Keep `frontmatter` short.
50
+ - Exit codes are the automation contract. The provider's self-report refines `status` only; never let it change the exit code.
51
+ - Provider CLIs change flags between releases. Verify with `tazuna run --role X --dry-run` after upgrading a CLI, and keep `experimental = true` on definitions nobody has run end to end.
52
+
53
+ ## References
54
+
55
+ - Architecture: `docs/architecture.md` · Security model: `docs/security.md` · Result contract: `docs/handoff.md`