|
11 | 11 |
|
12 | 12 | import pytest |
13 | 13 |
|
14 | | -from maxim.proprioception.pain import PainSignal, PainType |
| 14 | +from maxim.proprioception.pain import PainType |
15 | 15 | from maxim.proprioception.perceived_pain import ( |
16 | 16 | PerceivedPainAssessor, |
17 | 17 | SensitivePathPrior, |
18 | 18 | _path_matches_prior, |
19 | 19 | extract_paths_from_params, |
20 | 20 | tool_to_operation, |
21 | 21 | ) |
| 22 | +from maxim.reactions.types import Reaction |
22 | 23 | from maxim.runtime.pain_interceptor import ( |
23 | 24 | AnticipatoryPainExecutor, |
24 | 25 | PainInterceptorExecutor, |
25 | 26 | ) |
26 | 27 |
|
27 | 28 |
|
| 29 | +class _ReactionBus: |
| 30 | + """Minimal ReactionBus stand-in that just records published reactions.""" |
| 31 | + |
| 32 | + def __init__(self) -> None: |
| 33 | + self.reactions: list[Reaction] = [] |
| 34 | + |
| 35 | + def publish(self, reaction: Reaction) -> None: |
| 36 | + self.reactions.append(reaction) |
| 37 | + |
| 38 | + |
28 | 39 | class _Bus: |
29 | | - """Minimal PainBus stand-in that just records published signals.""" |
| 40 | + """Minimal PainBus stand-in with reaction_bus attribute (Phase 2b).""" |
30 | 41 |
|
31 | 42 | def __init__(self) -> None: |
32 | | - self.signals: list[PainSignal] = [] |
| 43 | + self.reaction_bus = _ReactionBus() |
33 | 44 |
|
34 | | - def publish(self, signal: PainSignal) -> None: |
35 | | - self.signals.append(signal) |
| 45 | + @property |
| 46 | + def signals(self) -> list[Reaction]: |
| 47 | + """Backward-compat alias so existing tests can still say bus.signals.""" |
| 48 | + return self.reaction_bus.reactions |
36 | 49 |
|
37 | 50 |
|
38 | 51 | class _Result: |
@@ -318,8 +331,8 @@ def test_fires_on_sensitive_read(self): |
318 | 331 | exe = PainInterceptorExecutor(_Executor(), pain_bus=bus) |
319 | 332 | exe.execute({"tool_name": "read_file", "params": {"path": "/etc/shadow"}}) |
320 | 333 | assert len(bus.signals) == 1 |
321 | | - assert bus.signals[0].pain_type == PainType.EXTERNAL_SIGNAL |
322 | | - assert bus.signals[0].context["kind"] == "consequence" |
| 334 | + assert bus.signals[0].kind == "pain" |
| 335 | + assert bus.signals[0].source == "pain_interceptor:external_signal" |
323 | 336 | assert bus.signals[0].intensity >= 0.9 |
324 | 337 |
|
325 | 338 | def test_does_not_fire_on_safe_path(self): |
@@ -349,7 +362,8 @@ def test_bash_rm_rf_fires_delete(self): |
349 | 362 | } |
350 | 363 | ) |
351 | 364 | assert len(bus.signals) == 1 |
352 | | - assert bus.signals[0].context["operation"] == "delete" |
| 365 | + assert bus.signals[0].kind == "pain" |
| 366 | + assert bus.signals[0].source == "pain_interceptor:external_signal" |
353 | 367 |
|
354 | 368 | def test_events_recorded(self): |
355 | 369 | bus = _Bus() |
@@ -410,8 +424,9 @@ def test_both_layers_fire_on_sensitive_access(self): |
410 | 424 | {"tool_name": "read_file", "params": {"path": "/etc/shadow"}}, |
411 | 425 | ) |
412 | 426 | assert len(bus.signals) == 2 |
413 | | - kinds = {s.context.get("kind") for s in bus.signals} |
414 | | - assert kinds == {"anticipated", "consequence"} |
| 427 | + sources = {s.source for s in bus.signals} |
| 428 | + assert "perceived_pain:anticipated" in sources |
| 429 | + assert "pain_interceptor:external_signal" in sources |
415 | 430 |
|
416 | 431 | def test_anticipation_without_consequence_on_safe_path(self): |
417 | 432 | bus = _Bus() |
|
0 commit comments