diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 71f1bb21..9a16194f 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -16,3 +16,8 @@ ## Testing + +## Changelog + + diff --git a/AGENTS.md b/AGENTS.md index d9990fdf..3689ae9b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -50,7 +50,7 @@ Two orthogonal seams: **which CLI** (adapter axis: `adapters/base.py` `CodingCLI | `tui/` | Textual dashboard (`tui` extra); observer/launcher only — never runs engines in-process | | `plugins/` | manifest-driven extension layer (`plugin.toml`, trust tiers, hook bus) | -~25 further leaf modules — read the module docstring before assuming. Deeper maps: [docs/FEATURES.md](docs/FEATURES.md), [docs/adapter-authoring-guide.md](docs/adapter-authoring-guide.md), [docs/multiplexer-backends.md](docs/multiplexer-backends.md). +28 further leaf modules — read the module docstring before assuming. Deeper maps: [docs/FEATURES.md](docs/FEATURES.md), [docs/adapter-authoring-guide.md](docs/adapter-authoring-guide.md), [docs/multiplexer-backends.md](docs/multiplexer-backends.md). ## Testing diff --git a/CHANGELOG.md b/CHANGELOG.md index ba4774ed..01adcd6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -322,6 +322,20 @@ whose seams had diverged enough that several ports needed a different fix, and t ### Fixed +- **Contributor docs now name every obligation CI enforces.** `CONTRIBUTING.md` never mentioned + `pyright` or the CHANGELOG, so its verify step — "`trunk check` and `uv run pytest -q` both + pass" — sent contributors into a CI failure on the dedicated typecheck job. It now carries + `uv run pyright`, the `## [Unreleased]` CHANGELOG contract, the Windows `PYTHONUTF8=1` + requirement and the Python 3.11 floor, the real extras (`tui`, `non-linux`, `opencode` — it + claimed only `tui`), and `scripts/release.py`'s two-phase `prepare`/`publish` flow. The pull + request template gains a matching Changelog section, as does CONTRIBUTING's copy of it. + +- **The docs no longer describe a state the code has left behind.** The README's command table + gained `bmad-loop adapters`, and its docs list gained five missing guides plus `docs/README.md`, + the full index. `docs/FEATURES.md` now covers all 15 policy sections rather than 12 — `[dev]` was + documented nowhere. The roadmap's native-Windows entry read `planned` while its own body said + psmux had shipped, and `AGENTS.md` counted `~25` leaf modules where there are 28. + - **A failed ledger write can no longer empty the deferred-work ledger (#328).** `Path.write_text` truncates the file and only then encodes, so any failure in that window — an unencodable value, `ENOSPC`, `EIO` — left a zero-byte ledger with every entry gone. `append_decision`, @@ -2653,8 +2667,6 @@ for antigravity`, immediately after a successful `init --cli antigravity`). Both Still pending live E2E and a `usage_parser` — `probe-adapter` captures the token schema to write one. -### Docs - - **Adapter authoring guide.** New [adapter authoring guide](docs/adapter-authoring-guide.md) walks through finalizing a CLI profile with `probe-adapter` (scan vs probe, the PII model, and the parser-writing loop); `probe-adapter` is added to both command references. @@ -2727,6 +2739,9 @@ for antigravity`, immediately after a successful `init --cli antigravity`). Both - **MIT license + open-source community files.** The project is now MIT-licensed (© BMad Code, LLC) with a trademark notice, and ships `CONTRIBUTING`, `SECURITY`, `CODE_OF_CONDUCT`, and GitHub issue/PR templates as it becomes a first-class citizen in the BMAD org. +- **Uninstall procedure.** The [setup guide](docs/setup-guide.md#uninstalling) now documents a + full teardown — reclaim disk, remove `.automator/`, skills, hooks, and gitignore lines, then + `uv tool uninstall`. ### Changed @@ -2737,12 +2752,6 @@ for antigravity`, immediately after a successful `init --cli antigravity`). Both [bmad-automator](https://github.com/bmad-code-org/bmad-automator) project is unrelated and stays as-is. Re-run `uv tool upgrade bmad-auto --reinstall` to move an existing install onto the new name. -### Docs - -- **Uninstall procedure.** The [setup guide](docs/setup-guide.md#uninstalling) now documents a - full teardown — reclaim disk, remove `.automator/`, skills, hooks, and gitignore lines, then - `uv tool uninstall`. - ## [0.5.0] — 2026-06-20 ### Added @@ -3117,4 +3126,4 @@ enforced in CI. [0.3.1]: https://github.com/bmad-code-org/bmad-loop/commit/37dd884efaff [0.3.0]: https://github.com/bmad-code-org/bmad-loop/commit/11a9d5a067a6 [0.2.0]: https://github.com/bmad-code-org/bmad-loop/commit/823adae1c047 -[0.1.0]: https://github.com/bmad-code-org/bmad-loop/commit/f0550a842210 +[0.1.0]: https://github.com/bmad-code-org/bmad-loop/commit/274f0f5b6fd2 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e8339b41..c2076e13 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -74,23 +74,41 @@ After searching, use the [feature request template](https://github.com/bmad-code ## Development Setup -bmad-loop is a Python project managed with [uv](https://docs.astral.sh/uv/). +bmad-loop is a Python project managed with [uv](https://docs.astral.sh/uv/). **Python 3.11 is the floor.** ```bash git clone https://github.com/YOUR-USERNAME/bmad-loop.git cd bmad-loop -uv sync --all-extras # installs deps + the [tui] extra + dev tools (pytest, ruff) -uv run pytest -q # unit + adapter scenarios + tmux integration +uv sync --all-extras # deps + all three extras (tui, non-linux, opencode) + dev tools +uv run pytest -q # unit + adapter scenarios + tmux integration (-n auto to parallelize) +uv run pyright # typecheck — CI runs this same pinned version as its own job ``` +Never `pip install` — uv owns the environment. If you change dependencies, edit `pyproject.toml` and run `uv lock`; CI uses `uv sync --locked` and fails on a stale lock. The pyright version is pinned exactly in the `dev` group, so bump it deliberately — never with `uv lock --upgrade`. + +> **On Windows**, set `PYTHONUTF8=1` before running the suite — `tests/conftest.py` raises a `UsageError` without it. + Linting and formatting run through [trunk](https://trunk.io) (ruff, black, isort, prettier, markdownlint, and more). **Run `trunk check` before pushing** — a pre-push hook enforces it, so formatting/lint failures surface locally instead of in CI: ```bash trunk fmt # auto-format changed files -trunk check # lint + format verification (what CI runs) +trunk check # lint + format verification, no path filter (what CI runs) ``` -Releases are cut by maintainers. The version field is validated in CI; if you touch it, run `uv run --no-project python scripts/sync_version.py --check`. +### CHANGELOG + +**Every user-visible change needs a CHANGELOG entry.** Add it under the `## [Unreleased]` heading in [CHANGELOG.md](CHANGELOG.md), and only under one of the six [Keep a Changelog](https://keepachangelog.com) subsections — `Added`, `Changed`, `Deprecated`, `Removed`, `Fixed`, `Security`. Keep entries terse, scannable, and imperative. + +Never open a new `## [X.Y.Z]` section yourself: a release _promotes_ `## [Unreleased]` into the version heading and reopens an empty one. `scripts/release.py check` enforces this contract in CI (the `version-sync` job). The full rule is in [AGENTS.md](AGENTS.md#repo-hygiene). + +### Releases + +Releases are cut by maintainers with `scripts/release.py`, which is two-phase: + +- **`prepare X.Y.Z`** runs on a release branch — it validates that the CHANGELOG's `## [Unreleased]` section was promoted into `## [X.Y.Z]`, stamps the version everywhere via `sync_version.py`, regenerates TUI assets when they changed, and commits, leaving the branch ready for a PR. +- **`publish`** runs on `main` after that PR merges (driven by `.github/workflows/release.yml`) — it creates the tag and GitHub release from the CHANGELOG, and is idempotent. + +Version strings are stamped only by `scripts/sync_version.py`; never hand-edit them in `pyproject.toml`, `module.yaml`, `marketplace.json`, or `uv.lock`. The version is validated in CI — if you touch it, run `uv run --no-project python scripts/sync_version.py --check`. --- @@ -120,10 +138,11 @@ We will reject PRs that read like raw LLM output: bulk refactors nobody asked fo 2. **Clone** your fork: `git clone https://github.com/YOUR-USERNAME/bmad-loop.git` 3. **Create a branch**: `git checkout -b fix/description` or `git checkout -b feature/description` 4. **Make changes** — keep them focused -5. **Verify**: `trunk check` and `uv run pytest -q` both pass -6. **Commit**: `git commit -m "fix: correct typo in README"` -7. **Push**: `git push origin fix/description` -8. **Open PR** from your fork on GitHub +5. **Changelog**: add an entry under `## [Unreleased]` in [CHANGELOG.md](CHANGELOG.md) for any user-visible change +6. **Verify**: `trunk check`, `uv run pytest -q`, and `uv run pyright` all pass — CI runs all three +7. **Commit**: `git commit -m "fix: correct typo in README"` +8. **Push**: `git push origin fix/description` +9. **Open PR** from your fork on GitHub ### PR Description Template @@ -144,6 +163,10 @@ Fixes #[issue number] ## Testing [1-2 sentences on how you tested this] + +## Changelog + +[Entry added under `## [Unreleased]` in CHANGELOG.md, under one of: Added, Changed, Deprecated, Removed, Fixed, Security. Write "n/a" if nothing user-visible changed.] ``` **Keep it under 200 words.** diff --git a/README.md b/README.md index 73e1ed59..7cc4e8f5 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,7 @@ bmad-loop tui # …or drive everything from the dashboard | `bmad-loop init` | Install the bundled `bmad-loop-*` skills, the hook relay, `.bmad-loop/policy.toml`, and a gitignore for the runs dir, plugin caches, and policy.toml itself (policy is per-machine — see the CHANGELOG migration note for repos initialized before this). `--cli ` (repeatable) targets specific agents; `--no-skills` / `--force-skills` control skill copying. | | `bmad-loop validate` | Preflight every prerequisite: BMAD config, sprint-status, git, CLI binary, hook registration, and a platform check that reports the selected multiplexer's readiness and process host (listing every registered backend when more than one is detected). `--spec ` validates that folder's `stories.yaml` for a stories-mode run instead of the sprint-status queue. `--json` emits the preflight as a stable machine-readable document (see [Scripting `validate`](#scripting-validate)). | | `bmad-loop mux` | List registered terminal-multiplexer backends — platform match, availability, version, and which one is selected (and why). `mux set ` persists a machine-scoped choice into `.bmad-loop/policy.toml` (`--clear` reverts to auto-select, `--force` allows a name that only registers on the target machine); the `BMAD_LOOP_MUX_BACKEND` env var outranks it. | +| `bmad-loop adapters` | List registered coding-CLI adapter **kinds** — name, builtin/external, whether the family drives a multiplexer, and which profiles select each — the CLI axis's counterpart to `mux`. Unlike `mux` there is no global choice to persist: a kind is selected per profile by its `adapter` field. A profile naming an unregistered kind, and any out-of-tree adapter/profile package that failed to load, get a `warning:` on stderr. | | `bmad-loop run` | Drive the dev → review → verify → commit loop. `--epic N`, `--story KEY`, `--max-stories N`, `--dry-run`. `--spec ` forces **stories mode** (folder+id dispatch off `/stories.yaml`), overriding `[stories].source`; `--story` then filters by story id. | | `bmad-loop sweep` | Triage + execute open `deferred-work.md` entries. `--no-prompt`, `--decisions-only`, `--max-bundles N`, `--repeat`, `--max-cycles N`, `--dry-run`. | | `bmad-loop resume ` | Continue a run paused at a gate, escalation, or interruption. | @@ -101,6 +102,12 @@ Every command takes `--project ` (default: the current directory). Any ` One subcommand is deliberately left out of the table: `bmad-loop relay ` writes a single +> session event file from a coding-CLI hook payload on stdin. Its own help calls it "a hook target +> for machines, not a command to run by hand" — it takes no `--project`, and `bmad-loop init` +> currently registers the copied workspace relay (`.bmad-loop/bmad_loop_hook.py`) instead, so no +> installed hook reaches the console script today. Never invoke it yourself. + ## The TUI ```bash @@ -419,6 +426,14 @@ on_status_contradiction = "escalate" # sign-off the orchestrator recorded at dev time) pauses the run naming # both sides; "retry" = legacy — burn review cycles, then defer + roll back +[dev] # which inner dev skill the orchestrator drives +skill = "bmad-dev-auto" # the only supported value — the generic upstream dev primitive. + # Retained as the seam for a future alternative dev skill, and it is + # NOT the name sessions are dispatched with: upstream renamed the + # primitive to bmad-build-auto, so the invoked name is resolved from + # what is on disk and projects on either era work with this untouched. + # No settings-schema entry: edit it here, not in the TUI editor. + [adapter] name = "claude" # CLI profile: claude | codex | gemini | copilot | antigravity | opencode-http (alias: opencode) | custom model = "" # empty = CLI default (opencode-http wants "provider/model") @@ -663,14 +678,21 @@ The hero **demo GIF** (`docs/images/demo.gif`) is generated the same headless wa ## Documentation +**[docs/README.md](docs/README.md) is the full index** — every guide, grouped by when you need it. The highlights: + - **[docs/FEATURES.md](docs/FEATURES.md)** — full feature & functionality list and the capability matrix (feature → problem addressed). - **[docs/setup-guide.md](docs/setup-guide.md)** — installing the module + the `/bmad-loop-setup` walkthrough. - **[docs/tui-guide.md](docs/tui-guide.md)** — the complete TUI reference. - **[src/bmad_loop/data/skills/README.md](src/bmad_loop/data/skills/README.md)** — the `bmad-loop` skill module overview. - **[docs/ROADMAP.md](docs/ROADMAP.md)** — planned/deferred orchestrator work and the rationale behind it. +- **[docs/multiplexer-backends.md](docs/multiplexer-backends.md)** — which backend drives your agent sessions, how selection resolves, and how external backends register. - **[docs/adapter-authoring-guide.md](docs/adapter-authoring-guide.md)** — authoring CLI adapters & profiles (and transport backends). - **[docs/plugin-authoring-guide.md](docs/plugin-authoring-guide.md)** — authoring plugins (hooks, workflows, settings). - **[docs/game-engine-plugin-guide.md](docs/game-engine-plugin-guide.md)** — the game-engine plugin shape (Unity reference). +- **[docs/game-engine-mcp-guide.md](docs/game-engine-mcp-guide.md)** — Editor-MCP specifics for the bundled Unity plugin, and its `BMAD_LOOP_*` env-var reference. +- **[docs/tea-plugin-guide.md](docs/tea-plugin-guide.md)** — the bundled `tea` plugin and the test-architecture steps it injects. +- **[docs/porting-to-a-new-os.md](docs/porting-to-a-new-os.md)** — the four OS seams and what a native-Windows port costs end to end. +- **[docs/testing.md](docs/testing.md)** — the layer model, fixture/ablation doctrines, quality guards, and the zero-token invariant. ## Contributing diff --git a/docs/FEATURES.md b/docs/FEATURES.md index 5673d0a6..682bc0cb 100644 --- a/docs/FEATURES.md +++ b/docs/FEATURES.md @@ -165,7 +165,8 @@ See [README.md](../README.md) for the narrative overview and [setup-guide.md](se ### Configuration (`.bmad-loop/policy.toml`) - Single policy file written by `init`, stamped into the run at every engine start — `run`, `sweep`, `resume` — so it always describes the policy that process enforces (applies to new runs and resumes; editable live from the TUI). -- Sections: `[gates]`, `[limits]`, `[verify]`, `[notify]`, `[review]`, `[adapter]` (+ per-stage), `[sweep]`, `[scm]` (worktree isolation + merge-back), `[cleanup]` (run-dir retention + disk reclamation), `[plugins]` (trust allowlist + per-plugin `[plugins.]` config — e.g. the opt-in game-engine layer via `[plugins.unity]`, off by default), `[tui]` (`low_frame_rate` for slow/SSH links; persisted dashboard pane sizes). +- Sections — all 15: `[gates]`, `[limits]`, `[verify]`, `[notify]`, `[review]`, `[stories]` (which planning pipeline drives the loop: sprint-status or a typed `stories.yaml`), `[dev]` (see below), `[adapter]` (+ per-stage `[adapter.dev|review|triage]`), `[sweep]`, `[scm]` (worktree isolation + merge-back), `[cleanup]` (run-dir retention + disk reclamation), `[plugins]` (trust allowlist + per-plugin `[plugins.]` config — e.g. the opt-in game-engine layer via `[plugins.unity]`, off by default), `[tui]` (`low_frame_rate` for slow/SSH links; persisted dashboard pane sizes), `[operator]` (whether a dev session may park a story at `awaiting-operator`), `[mux]` (machine-scoped multiplexer backend choice). +- `[dev] skill` names the inner dev skill the orchestrator drives. `"bmad-dev-auto"` — the generic upstream dev primitive — is the only accepted value; the field is retained as the seam for a future alternative dev skill, and any other value is rejected at load. It is **not** the name sessions are dispatched with: upstream renamed the primitive to `bmad-build-auto`, so the invoked name is resolved from what is actually installed and a project on either era works with this field untouched. It has no entry in the core settings schema, so it is edited in the file rather than from the TUI settings editor. - Tunable limits: `max_review_cycles`, `max_dev_attempts`, `max_followup_reviews`, `session_timeout_min`, `git_timeout_s`, `teardown_grace_s` (one shared budget bounding the verified window kill _and_ the follow-on reap of any straggler descendant the session detached — e.g. a `setsid` background writer — combined; whatever remains after the window dies is what the straggler reap gets, before the worktree is merged and removed), `stop_without_result_nudges`, `dev_stall_grace_s`, `dev_stall_nudges`, `dev_stall_nudges_cap`, `workflow_stall_nudges_cap`, `max_tokens_per_story`. ### TUI dashboard diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index fd6a8705..06205215 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -3,13 +3,13 @@ Forward-looking work for the orchestrator itself — design intent and rationale for features we've deliberately deferred, so the "why" survives between sessions. -Status legend: **planned** (agreed, not started) · **exploring** (shape still open) · **blocked** (waiting on an external dependency). +Status legend: **planned** (agreed, not started) · **in progress** (started; some pieces shipped, the headline goal not yet met) · **exploring** (shape still open) · **blocked** (waiting on an external dependency). --- ## Native Windows multiplexer backend -**Status:** planned · **Foundation:** the full platform-seam series landed (multiplexer registry + `BaseTmuxBackend` + `ProcessHost` + hook interpreter + validate preflight, v0.7.6; availability-aware backend selection + `bmad-loop mux`, #87; out-of-tree backend discovery via `bmad_loop.mux_backends` entry points; original seam v0.7.0) · a first non-tmux-family, native-Windows-capable backend — **herdr** — ships end-to-end on POSIX as the external [bmad-loop-adapter-herdr](https://github.com/pbean/bmad-loop-adapter-herdr) (the win32 `agent.start` launch path is tracked there) +**Status:** in progress — the platform seams and a native-Windows backend (`psmux`, held by a real-Windows live gate) have **shipped in-tree**; still open are the second tmux-family backend `tmux-windows` (#85), herdr's win32 launch path, and the open questions at the end of this section · **Foundation:** the full platform-seam series landed (multiplexer registry + `BaseTmuxBackend` + `ProcessHost` + hook interpreter + validate preflight, v0.7.6; availability-aware backend selection + `bmad-loop mux`, #87; out-of-tree backend discovery via `bmad_loop.mux_backends` entry points; original seam v0.7.0) · a first non-tmux-family, native-Windows-capable backend — **herdr** — ships end-to-end on POSIX as the external [bmad-loop-adapter-herdr](https://github.com/pbean/bmad-loop-adapter-herdr) (the win32 `agent.start` launch path is tracked there) The orchestrator no longer fuses tmux into the engine. All session/window/pane operations go through a single `TerminalMultiplexer` ABC (`src/bmad_loop/adapters/multiplexer.py`),