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. 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}" + )