From 866130888626971a801d6d726d2d3eb8ad1222eb Mon Sep 17 00:00:00 2001 From: Ricky Jones Date: Sun, 22 Feb 2026 11:48:16 +0000 Subject: [PATCH 01/13] fix: block reset() from RED to align with canonical terminal invariant Updated reset method to prevent resetting from RED state. --- stop_machine.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/stop_machine.py b/stop_machine.py index 3872443..c66369e 100644 --- a/stop_machine.py +++ b/stop_machine.py @@ -1,3 +1,7 @@ +# CANONICAL SOURCE: LalaSkye/constraint-workshop/stop_machine.py +# PINNED COMMIT: 8d04cbc1e8e6576641962d5d3c866b0517ad596e +# Semantics aligned to canonical: RED is terminal, reset() blocked from RED. + """A deterministic three-state stop controller. States: GREEN -> AMBER -> RED @@ -87,10 +91,11 @@ def transition_to(self, target: State) -> State: return self._state def reset(self) -> State: - """Reset the machine to GREEN. - - Returns the new state (always GREEN). - """ + """Reset the machine to GREEN. Forbidden once RED is reached.""" + if self.is_terminal: + raise TerminalStateError( + f"Cannot reset: {self._state.value} is terminal." + ) self._state = State.GREEN return self._state From ec3d1821cf64ccd5c880f6089a7ff7801e0ba29a Mon Sep 17 00:00:00 2001 From: Ricky Jones Date: Sun, 22 Feb 2026 11:52:56 +0000 Subject: [PATCH 02/13] refactor: rename primitives/stop-machine to stop-machine-v0 --- primitives/{stop-machine => stop-machine-v0}/stop_machine.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename primitives/{stop-machine => stop-machine-v0}/stop_machine.py (100%) diff --git a/primitives/stop-machine/stop_machine.py b/primitives/stop-machine-v0/stop_machine.py similarity index 100% rename from primitives/stop-machine/stop_machine.py rename to primitives/stop-machine-v0/stop_machine.py From e127ed89c65e89ac22890376534775546dac3a2c Mon Sep 17 00:00:00 2001 From: Ricky Jones Date: Sun, 22 Feb 2026 11:54:46 +0000 Subject: [PATCH 03/13] refactor: move test_stop_machine.py to stop-machine-v0 --- primitives/{stop-machine => stop-machine-v0}/test_stop_machine.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename primitives/{stop-machine => stop-machine-v0}/test_stop_machine.py (100%) diff --git a/primitives/stop-machine/test_stop_machine.py b/primitives/stop-machine-v0/test_stop_machine.py similarity index 100% rename from primitives/stop-machine/test_stop_machine.py rename to primitives/stop-machine-v0/test_stop_machine.py From b13e66aed32f3ecbdc5840c3237987704d96b4f9 Mon Sep 17 00:00:00 2001 From: Ricky Jones Date: Sun, 22 Feb 2026 11:55:33 +0000 Subject: [PATCH 04/13] refactor: complete stop-machine -> stop-machine-v0 rename --- primitives/{stop-machine => stop-machine-v0}/README.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename primitives/{stop-machine => stop-machine-v0}/README.md (100%) diff --git a/primitives/stop-machine/README.md b/primitives/stop-machine-v0/README.md similarity index 100% rename from primitives/stop-machine/README.md rename to primitives/stop-machine-v0/README.md From f53f9f2fd840a7023146ff637ac49afb63db1b4c Mon Sep 17 00:00:00 2001 From: Ricky Jones Date: Sun, 22 Feb 2026 11:56:16 +0000 Subject: [PATCH 05/13] refactor: rename primitives/authority-gate to authority-gate-v0 --- primitives/{authority-gate => authority-gate-v0}/gate.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename primitives/{authority-gate => authority-gate-v0}/gate.py (100%) diff --git a/primitives/authority-gate/gate.py b/primitives/authority-gate-v0/gate.py similarity index 100% rename from primitives/authority-gate/gate.py rename to primitives/authority-gate-v0/gate.py From 7244d1da43ec1245931b3938428a79408894c4c6 Mon Sep 17 00:00:00 2001 From: Ricky Jones Date: Sun, 22 Feb 2026 11:56:50 +0000 Subject: [PATCH 06/13] refactor: move test_gate.py to authority-gate-v0 --- primitives/{authority-gate => authority-gate-v0}/test_gate.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename primitives/{authority-gate => authority-gate-v0}/test_gate.py (100%) diff --git a/primitives/authority-gate/test_gate.py b/primitives/authority-gate-v0/test_gate.py similarity index 100% rename from primitives/authority-gate/test_gate.py rename to primitives/authority-gate-v0/test_gate.py From a96b17805fbc7911e7cf2bdd8d50b7a470339578 Mon Sep 17 00:00:00 2001 From: Ricky Jones Date: Sun, 22 Feb 2026 11:57:31 +0000 Subject: [PATCH 07/13] refactor: complete authority-gate -> authority-gate-v0 rename --- primitives/{authority-gate => authority-gate-v0}/README.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename primitives/{authority-gate => authority-gate-v0}/README.md (100%) diff --git a/primitives/authority-gate/README.md b/primitives/authority-gate-v0/README.md similarity index 100% rename from primitives/authority-gate/README.md rename to primitives/authority-gate-v0/README.md From e3c3736bebac33840d179650584cb7803cdc440e Mon Sep 17 00:00:00 2001 From: Ricky Jones Date: Sun, 22 Feb 2026 11:59:16 +0000 Subject: [PATCH 08/13] fix: update demo imports for renamed primitive folders --- examples/demo_stop_machine.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/demo_stop_machine.py b/examples/demo_stop_machine.py index 5316531..c3a5690 100644 --- a/examples/demo_stop_machine.py +++ b/examples/demo_stop_machine.py @@ -2,7 +2,7 @@ import sys from pathlib import Path -sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "primitives" / "stop-machine")) +sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "primitives" / "stop-machine-v0")) from stop_machine import Event, StopMachine # noqa: E402 From 449260096677afe190b199a590dd04744c0340dd Mon Sep 17 00:00:00 2001 From: Ricky Jones Date: Sun, 22 Feb 2026 12:00:13 +0000 Subject: [PATCH 09/13] fix: update authority-gate demo import for v0 folder --- examples/demo_authority_gate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/demo_authority_gate.py b/examples/demo_authority_gate.py index 0450f7f..f8b221f 100644 --- a/examples/demo_authority_gate.py +++ b/examples/demo_authority_gate.py @@ -3,7 +3,7 @@ import sys from pathlib import Path -sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "primitives" / "authority-gate")) +sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "primitives" / "authority-gate-v0")) from gate import Authority, AuthorityGate # noqa: E402 From f61b663b57d0407fad76511325032b1fad06b127 Mon Sep 17 00:00:00 2001 From: Ricky Jones Date: Sun, 22 Feb 2026 12:02:06 +0000 Subject: [PATCH 10/13] ci: expand test runner to include root-level tests (NON-NEGOTIABLE) --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8b97713..689aeff 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,4 +25,4 @@ jobs: run: pip install pytest - name: Run tests - run: python -m pytest primitives -v + run: python -m pytest . -v From c0fd0a1f4b8e0bb0d4523c0ab09d9505097e07ad Mon Sep 17 00:00:00 2001 From: Ricky Jones Date: Sun, 22 Feb 2026 12:06:21 +0000 Subject: [PATCH 11/13] ci: fix test collection collision with --import-mode=importlib --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 689aeff..9d2d0ba 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,4 +25,4 @@ jobs: run: pip install pytest - name: Run tests - run: python -m pytest . -v + run: python -m pytest . -v --import-mode=importlib From 3e24af74577d3f947ba16e14651a5478416f36bc Mon Sep 17 00:00:00 2001 From: Ricky Jones Date: Sun, 22 Feb 2026 12:09:10 +0000 Subject: [PATCH 12/13] ci: split test steps to avoid name collision (primitives + root) --- .github/workflows/ci.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9d2d0ba..e76cc11 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,5 +24,7 @@ jobs: - name: Install dependencies run: pip install pytest - - name: Run tests - run: python -m pytest . -v --import-mode=importlib + - name: Run primitive tests + run: python -m pytest primitives -v + - name: Run root tests + run: python -m pytest test_stop_machine.py -v From 4e5168112782d14f42cf72a13705fa9051c6c38e Mon Sep 17 00:00:00 2001 From: Ricky Jones Date: Sun, 22 Feb 2026 12:12:49 +0000 Subject: [PATCH 13/13] test: update reset tests to expect TerminalStateError from RED Update reset tests to expect TerminalStateError on reset. --- test_stop_machine.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/test_stop_machine.py b/test_stop_machine.py index 773324b..bea8fa6 100644 --- a/test_stop_machine.py +++ b/test_stop_machine.py @@ -134,10 +134,8 @@ def test_reset_from_amber(): def test_reset_from_red(): m = StopMachine(State.RED) - result = m.reset() - assert result == State.GREEN - assert m.state == State.GREEN - assert not m.is_terminal + with pytest.raises(TerminalStateError): + m.reset() def test_reset_then_advance(): @@ -145,10 +143,8 @@ def test_reset_then_advance(): m.advance() m.advance() assert m.is_terminal - m.reset() - assert m.state == State.GREEN - m.advance() - assert m.state == State.AMBER + with pytest.raises(TerminalStateError): + m.reset() # --- repr ---