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 Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 8 additions & 1 deletion agents/core-expert.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,14 @@ Provide:
- Note: this applies specifically to amplifier-core (PyPI package), not all ecosystem repos
- The three files that must be bumped in sync: `pyproject.toml`, `crates/amplifier-core/Cargo.toml`, `bindings/python/Cargo.toml`
- The atomic script: `python scripts/bump_version.py X.Y.Z`
- **The E2E smoke test**: `./scripts/e2e-smoke-test.sh` must pass before tagging. It validates
the built wheel in an isolated Docker container with a real LLM session. This gate was added
after three incidents in v1.2.3/v1.2.4 where the wheel was broken but all unit/integration
tests passed.
- The tag push: `git tag vX.Y.Z && git push origin main --tags`
- Why: `v*` tag triggers `rust-core-wheels.yml` → PyPI publish; this is the only path to production
- **Incident recovery**: If a broken version reaches PyPI, see the Incident Playbook in
`context/release-mandate.md` — yank on PyPI, fix forward, never reuse a version number.

---

Expand Down Expand Up @@ -242,7 +248,8 @@ See @core:docs/contracts/[NAME]_CONTRACT.md for complete specification.
- **Two-implementation rule** before promoting anything
- **Backward compatibility** is sacred
- **Reference contract docs** - don't copy their content
- **Release gate is mandatory** — every merge to amplifier-core main requires a version bump, `v*` tag, and push before the next PR starts. See CORE_DEVELOPMENT_PRINCIPLES.md §10.
- **Release gate is mandatory** — every merge to amplifier-core main requires a version bump, E2E smoke test, `v*` tag, and push before the next PR starts. See CORE_DEVELOPMENT_PRINCIPLES.md §10.
- **E2E before tagging** — `./scripts/e2e-smoke-test.sh` must pass before any `v*` tag. Unit tests are necessary but not sufficient; the v1.2.3/v1.2.4 incidents proved that 549 passing tests don't guarantee the wheel works.

**Your Mantra**: "The center stays still so the edges can move fast. I help ensure the kernel remains tiny, stable, and boring."

Expand Down
4 changes: 1 addition & 3 deletions bindings/python/src/coordinator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ mod mount_points;
/// The `mount_points` dict is directly accessible and mutable from Python,
/// matching `ModuleCoordinator.mount_points` behavior that the ecosystem
/// (pytest_plugin, testing.py) depends on.
#[pyclass(name = "RustCoordinator", subclass)]
#[pyclass(name = "RustCoordinator", subclass, dict)]
pub(crate) struct PyCoordinator {
/// Rust kernel coordinator (for reset_turn, injection tracking, config).
pub(crate) inner: Arc<amplifier_core::Coordinator>,
Expand Down Expand Up @@ -478,5 +478,3 @@ impl PyCoordinator {
Ok(dict)
}
}


9 changes: 8 additions & 1 deletion bindings/python/tests/test_schema_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,14 @@ def test_rust_engine_has_version():
"""Verify _engine exposes __version__."""
from amplifier_core._engine import __version__

assert __version__ == "1.0.0"
# Version comes from env!("CARGO_PKG_VERSION") — must match Cargo.toml
assert __version__ is not None
assert isinstance(__version__, str)
assert len(__version__) > 0
# Verify it looks like a semver version (X.Y.Z)
parts = __version__.split(".")
assert len(parts) == 3, f"Expected semver X.Y.Z, got {__version__}"
assert all(p.isdigit() for p in parts), f"Expected numeric semver, got {__version__}"


def test_rust_engine_has_types():
Expand Down
58 changes: 56 additions & 2 deletions context/release-mandate.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,66 @@ This rule exists **specifically** because `amplifier-core` publishes to PyPI and
- `pyproject.toml` (line 3)
- `crates/amplifier-core/Cargo.toml` (line 3)
- `bindings/python/Cargo.toml` (line 3)
3. Commit, tag, and push:
3. Run the E2E smoke test (mandatory since v1.2.5):
```bash
./scripts/e2e-smoke-test.sh
```
This builds a wheel from local source, installs it in an isolated Docker container alongside
the real `amplifier` CLI, and runs a real LLM-powered session exercising tool dispatch,
agent delegation, and recipe execution. It catches:
- Import/attribute errors in the Rust↔Python bridge
- Session startup crashes
- Tool dispatch failures
- Any Python exception during a real agent loop

**Requirements:** Docker running, `ANTHROPIC_API_KEY` set (or in `~/.amplifier/keys.env`).
Takes ~5 minutes. **Do not tag until this passes.**

4. Commit, tag, and push:
```bash
git commit -am "chore: bump version to X.Y.Z"
git tag vX.Y.Z
git push origin main --tags
```
4. The `v*` tag triggers `rust-core-wheels.yml` → builds wheels for all platforms → publishes to PyPI.
5. The `v*` tag triggers `rust-core-wheels.yml` → builds wheels for all platforms → publishes to PyPI.

Full process details: `docs/CORE_DEVELOPMENT_PRINCIPLES.md` §10 — The Release Gate.

---

## Incident Playbook: When a Broken Version Reaches PyPI

*Added after the v1.2.3/v1.2.4 incidents (March 2026).*

### The Problem

Once a version is published to PyPI, `uv tool install amplifier` and `pip install amplifier-core`
serve it immediately. There is no "rollback" button. For `uv tool install` users specifically,
there is no fast local rollback — users must wait for a fix.

### The Playbook

1. **Yank the broken version on PyPI** (immediately, ~30 seconds):
- Go to https://pypi.org/manage/project/amplifier-core/release/X.Y.Z/
- Click "Options" → "Yank release"
- Add reason: "Broken: [brief description]"

Yanking tells pip/uv to skip this version for new installs. New `amplifier update`
invocations will resolve to the last non-yanked version.

2. **Fix forward** — do NOT try to reuse the yanked version number:
- Fix the bug on `main`
- Run the E2E smoke test (`./scripts/e2e-smoke-test.sh`)
- Bump to the next PATCH version
- Tag + push as normal

3. **Post-mortem**: Add the incident to the history below.

### Incident History

| Version | Date | Root Cause | Impact | Resolution |
|---------|------|-----------|--------|------------|
| v1.0.7→v1.0.8 | 2026-03-03 | RetryConfig break for provider-anthropic | Provider users broken | Emergency hotfix |
| v1.2.3 | 2026-03-16 | `session_state` crash — missing dict field on RustCoordinator | CLI startup crashed | Yanked |
| v1.2.4 | 2026-03-16 | `_tool_dispatch_context` crash — RustCoordinator lacked `__dict__` | All tool dispatch crashed | Yanked |
| v1.2.4 | 2026-03-16 | Version files not bumped before tagging | PyPI publish rejected (400) | Re-tagged |
13 changes: 11 additions & 2 deletions docs/CORE_DEVELOPMENT_PRINCIPLES.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ Each test layer has a distinct purpose:
| **Proto equivalence tests** | Proto expansion matches hand-written Rust types | `src/generated/equivalence_tests.rs` |
| **Python tests** | Behavioral compatibility — the PyO3 bridge works correctly | `tests/`, `bindings/python/tests/` |
| **Switchover tests** | Python API contract — same imports, same behavior after Rust migration | `bindings/python/tests/test_switchover_*.py` |
| **E2E smoke test** | Real-world validation — the built wheel works in an isolated install with real LLM calls | `scripts/e2e-smoke-test.sh` |

**The compiler is the first test.** If it compiles and clippy is clean, the structural correctness bar is already met. Tests then verify behavior, not shape.

Expand Down Expand Up @@ -192,14 +193,22 @@ This rule applies **specifically to amplifier-core** because of its PyPI distrib
- `crates/amplifier-core/Cargo.toml` (line 3)
- `bindings/python/Cargo.toml` (line 3)

3. **Commit, tag, and push:**
3. **Run the E2E smoke test** before tagging:
```bash
./scripts/e2e-smoke-test.sh
```
Validates the built wheel in an isolated Docker container with a real LLM session.
Requires Docker and `ANTHROPIC_API_KEY`. Do not tag until it passes.
See `context/release-mandate.md` for full details and incident playbook.

4. **Commit, tag, and push:**
```bash
git commit -am "chore: bump version to X.Y.Z"
git tag vX.Y.Z
git push origin main --tags
```

4. **Verify CI triggers.** The `v*` tag triggers `rust-core-wheels.yml`, which builds wheels for all platforms (Linux x86/aarch64, macOS, Windows) and publishes to PyPI. The next PR does not start until PyPI publish is confirmed.
5. **Verify CI triggers.** The `v*` tag triggers `rust-core-wheels.yml`, which builds wheels for all platforms (Linux x86/aarch64, macOS, Windows) and publishes to PyPI. The next PR does not start until PyPI publish is confirmed.

### Why the Script Exists

Expand Down
Loading
Loading