From c9136d3ae8691f9817fe3a38baf8a0853d625d42 Mon Sep 17 00:00:00 2001 From: Leynos Date: Sun, 30 Aug 2026 10:42:40 +0100 Subject: [PATCH 1/2] Test Rust state-machine guidance --- tests/test_agents_state_machine_guidance.py | 45 +++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 tests/test_agents_state_machine_guidance.py diff --git a/tests/test_agents_state_machine_guidance.py b/tests/test_agents_state_machine_guidance.py new file mode 100644 index 0000000..cf4fc8c --- /dev/null +++ b/tests/test_agents_state_machine_guidance.py @@ -0,0 +1,45 @@ +"""Validate conditional state-machine guidance in rendered AGENTS.md files.""" + +from __future__ import annotations + +from pathlib import Path + +import pytest +from pytest_copier.plugin import CopierFixture + +_STATE_MACHINE_GUIDANCE = ( + "Model mutually exclusive states explicitly", + "correlated booleans, `Option` fields, sentinel values", + "disguising an enum with state-specific payloads", + "Choose state representation by who selects the transition", + "prefer a runtime ADT", + "explicit stack or other runtime structure for unbounded state", + "Encapsulate transitions and mandatory finalization in the state owner", + "semantic transition/result enums", + "Keep genuinely independent booleans as booleans", + "not merely to avoid `bool`", +) + + +@pytest.mark.parametrize("use_rust", [False, True]) +def test_state_machine_guidance_is_limited_to_rust_extensions( + copier: CopierFixture, + tmp_path: Path, + *, + use_rust: bool, +) -> None: + """Require state-machine guidance exactly when the Rust extension is enabled.""" + variant = "rust" if use_rust else "python" + project = copier.copy( + tmp_path / variant, + project_name=f"StateMachine{variant.title()}", + package_name=f"state_machine_{variant}", + use_rust=use_rust, + ) + agents = (project / "AGENTS.md").read_text(encoding="utf-8") + + for fragment in _STATE_MACHINE_GUIDANCE: + assert (fragment in agents) is use_rust, ( + "expected state-machine guidance presence to match the Rust-extension " + f"setting for {fragment!r}" + ) From a3441fc53426c14d343c19de05b4e5f2137cca4a Mon Sep 17 00:00:00 2001 From: Leynos Date: Sun, 30 Aug 2026 10:43:58 +0100 Subject: [PATCH 2/2] Add Rust state-machine guidance --- template/AGENTS.md.jinja | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/template/AGENTS.md.jinja b/template/AGENTS.md.jinja index 520fe98..9629432 100644 --- a/template/AGENTS.md.jinja +++ b/template/AGENTS.md.jinja @@ -280,6 +280,22 @@ project: tailoring is needed. Combine approaches: use `newt-hype` for the common case, tuple structs for outliers, and `the-newtype` to unify behaviour when defining traits across wrappers. +- Model mutually exclusive states explicitly. When a state carrier contains + correlated booleans, `Option` fields, sentinel values, or fields that must + change together, ask whether they are disguising an enum with state-specific + payloads. +- Choose state representation by who selects the transition. When the caller + chooses a small, finite sequence of operations, typestate may make illegal + call sequences unrepresentable. When input, a parser token, frame, socket + event, channel event, or scheduler chooses the next state, prefer a runtime + ADT; use an explicit stack or other runtime structure for unbounded state. +- Encapsulate transitions and mandatory finalization in the state owner. + Prefer semantic transition/result enums over mutating several correlated + fields and returning flags; consuming methods should perform required + cleanup rather than making callers remember multi-step ceremony. +- Keep genuinely independent booleans as booleans. Introduce an ADT to remove + impossible combinations or express domain alternatives, not merely to + avoid `bool`. - Use `cap_std` and `cap_std::fs_utf8` / `camino` in place of `std::fs` and `std::path` for enhanced cross-platform support and capability oriented filesystem access.