Cosmic Ray Report

Date time: 27/03/2026 14:12:47

Total jobs: 1222

Complete: 1222 (100.00%)

Surviving mutants: 556 (45.50%)

operator: core/NumberReplacer, occurrence: 0
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -31,7 +31,7 @@
     "stampbot_http_request_duration_seconds",
     "HTTP request duration in seconds",
     ["method", "endpoint"],
-    buckets=(0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=( 1.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
 )
 
 http_request_size_bytes = Histogram(
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7f23b251f9d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:30: in <module>
    http_request_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7f23b076cad0>
source_buckets = (1.005, 0.01, 0.025, 0.05, 0.1, 0.25, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.66s
operator: core/NumberReplacer, occurrence: 1
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -31,7 +31,7 @@
     "stampbot_http_request_duration_seconds",
     "HTTP request duration in seconds",
     ["method", "endpoint"],
-    buckets=(0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=( -0.995, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
 )
 
 http_request_size_bytes = Histogram(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/NumberReplacer, occurrence: 2
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -31,7 +31,7 @@
     "stampbot_http_request_duration_seconds",
     "HTTP request duration in seconds",
     ["method", "endpoint"],
-    buckets=(0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.005, 1.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
 )
 
 http_request_size_bytes = Histogram(
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7f9b9dee39d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:30: in <module>
    http_request_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7f9b9c24cad0>
source_buckets = (0.005, 1.01, 0.025, 0.05, 0.1, 0.25, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.67s
operator: core/NumberReplacer, occurrence: 3
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -31,7 +31,7 @@
     "stampbot_http_request_duration_seconds",
     "HTTP request duration in seconds",
     ["method", "endpoint"],
-    buckets=(0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.005, -0.99, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
 )
 
 http_request_size_bytes = Histogram(
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7f13d40ef9d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:30: in <module>
    http_request_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7f13d235cad0>
source_buckets = (0.005, -0.99, 0.025, 0.05, 0.1, 0.25, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.67s
operator: core/NumberReplacer, occurrence: 4
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -31,7 +31,7 @@
     "stampbot_http_request_duration_seconds",
     "HTTP request duration in seconds",
     ["method", "endpoint"],
-    buckets=(0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.005, 0.01, 1.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
 )
 
 http_request_size_bytes = Histogram(
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7ff936c479d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:30: in <module>
    http_request_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7ff934f5cad0>
source_buckets = (0.005, 0.01, 1.025, 0.05, 0.1, 0.25, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.66s
operator: core/NumberReplacer, occurrence: 5
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -31,7 +31,7 @@
     "stampbot_http_request_duration_seconds",
     "HTTP request duration in seconds",
     ["method", "endpoint"],
-    buckets=(0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.005, 0.01, -0.975, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
 )
 
 http_request_size_bytes = Histogram(
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7f6fe452f9d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:30: in <module>
    http_request_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7f6fe285cad0>
source_buckets = (0.005, 0.01, -0.975, 0.05, 0.1, 0.25, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.66s
operator: core/NumberReplacer, occurrence: 6
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -31,7 +31,7 @@
     "stampbot_http_request_duration_seconds",
     "HTTP request duration in seconds",
     ["method", "endpoint"],
-    buckets=(0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.005, 0.01, 0.025, 1.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
 )
 
 http_request_size_bytes = Histogram(
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7f02c9ed39d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:30: in <module>
    http_request_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7f02c8258ad0>
source_buckets = (0.005, 0.01, 0.025, 1.05, 0.1, 0.25, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.66s
operator: core/NumberReplacer, occurrence: 7
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -31,7 +31,7 @@
     "stampbot_http_request_duration_seconds",
     "HTTP request duration in seconds",
     ["method", "endpoint"],
-    buckets=(0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.005, 0.01, 0.025, -0.95, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
 )
 
 http_request_size_bytes = Histogram(
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7efc271c79d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:30: in <module>
    http_request_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7efc25464ad0>
source_buckets = (0.005, 0.01, 0.025, -0.95, 0.1, 0.25, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.67s
operator: core/NumberReplacer, occurrence: 8
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -31,7 +31,7 @@
     "stampbot_http_request_duration_seconds",
     "HTTP request duration in seconds",
     ["method", "endpoint"],
-    buckets=(0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.005, 0.01, 0.025, 0.05, 1.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
 )
 
 http_request_size_bytes = Histogram(
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7f63a211f9d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:30: in <module>
    http_request_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7f63a0468ad0>
source_buckets = (0.005, 0.01, 0.025, 0.05, 1.1, 0.25, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.67s
operator: core/NumberReplacer, occurrence: 9
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -31,7 +31,7 @@
     "stampbot_http_request_duration_seconds",
     "HTTP request duration in seconds",
     ["method", "endpoint"],
-    buckets=(0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.005, 0.01, 0.025, 0.05, -0.9, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
 )
 
 http_request_size_bytes = Histogram(
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7f9aa72f79d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:30: in <module>
    http_request_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7f9aa555cad0>
source_buckets = (0.005, 0.01, 0.025, 0.05, -0.9, 0.25, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.66s
operator: core/NumberReplacer, occurrence: 10
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -31,7 +31,7 @@
     "stampbot_http_request_duration_seconds",
     "HTTP request duration in seconds",
     ["method", "endpoint"],
-    buckets=(0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.005, 0.01, 0.025, 0.05, 0.1, 1.25, 0.5, 1.0, 2.5, 5.0, 10.0),
 )
 
 http_request_size_bytes = Histogram(
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7fe569f4b9d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:30: in <module>
    http_request_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7fe568154ad0>
source_buckets = (0.005, 0.01, 0.025, 0.05, 0.1, 1.25, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.67s
operator: core/NumberReplacer, occurrence: 11
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -31,7 +31,7 @@
     "stampbot_http_request_duration_seconds",
     "HTTP request duration in seconds",
     ["method", "endpoint"],
-    buckets=(0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.005, 0.01, 0.025, 0.05, 0.1, -0.75, 0.5, 1.0, 2.5, 5.0, 10.0),
 )
 
 http_request_size_bytes = Histogram(
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7f0abdd9b9d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:30: in <module>
    http_request_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7f0abc04cad0>
source_buckets = (0.005, 0.01, 0.025, 0.05, 0.1, -0.75, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.67s
operator: core/NumberReplacer, occurrence: 12
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -31,7 +31,7 @@
     "stampbot_http_request_duration_seconds",
     "HTTP request duration in seconds",
     ["method", "endpoint"],
-    buckets=(0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 1.5, 1.0, 2.5, 5.0, 10.0),
 )
 
 http_request_size_bytes = Histogram(
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7f2a30e3b9d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:30: in <module>
    http_request_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7f2a2f15cad0>
source_buckets = (0.005, 0.01, 0.025, 0.05, 0.1, 0.25, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.65s
operator: core/NumberReplacer, occurrence: 13
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -31,7 +31,7 @@
     "stampbot_http_request_duration_seconds",
     "HTTP request duration in seconds",
     ["method", "endpoint"],
-    buckets=(0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.005, 0.01, 0.025, 0.05, 0.1, 0.25, -0.5, 1.0, 2.5, 5.0, 10.0),
 )
 
 http_request_size_bytes = Histogram(
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7f183b81f9d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:30: in <module>
    http_request_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7f1839a2cad0>
source_buckets = (0.005, 0.01, 0.025, 0.05, 0.1, 0.25, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.67s
operator: core/NumberReplacer, occurrence: 14
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -31,7 +31,7 @@
     "stampbot_http_request_duration_seconds",
     "HTTP request duration in seconds",
     ["method", "endpoint"],
-    buckets=(0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 2.0, 2.5, 5.0, 10.0),
 )
 
 http_request_size_bytes = Histogram(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/NumberReplacer, occurrence: 15
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -31,7 +31,7 @@
     "stampbot_http_request_duration_seconds",
     "HTTP request duration in seconds",
     ["method", "endpoint"],
-    buckets=(0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 0.0, 2.5, 5.0, 10.0),
 )
 
 http_request_size_bytes = Histogram(
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7f1d5f2cf9d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:30: in <module>
    http_request_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7f1d5d560ad0>
source_buckets = (0.005, 0.01, 0.025, 0.05, 0.1, 0.25, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.67s
operator: core/NumberReplacer, occurrence: 16
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -31,7 +31,7 @@
     "stampbot_http_request_duration_seconds",
     "HTTP request duration in seconds",
     ["method", "endpoint"],
-    buckets=(0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 3.5, 5.0, 10.0),
 )
 
 http_request_size_bytes = Histogram(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/NumberReplacer, occurrence: 17
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -31,7 +31,7 @@
     "stampbot_http_request_duration_seconds",
     "HTTP request duration in seconds",
     ["method", "endpoint"],
-    buckets=(0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 1.5, 5.0, 10.0),
 )
 
 http_request_size_bytes = Histogram(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/NumberReplacer, occurrence: 18
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -31,7 +31,7 @@
     "stampbot_http_request_duration_seconds",
     "HTTP request duration in seconds",
     ["method", "endpoint"],
-    buckets=(0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 6.0, 10.0),
 )
 
 http_request_size_bytes = Histogram(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.64s
operator: core/NumberReplacer, occurrence: 19
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -31,7 +31,7 @@
     "stampbot_http_request_duration_seconds",
     "HTTP request duration in seconds",
     ["method", "endpoint"],
-    buckets=(0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 4.0, 10.0),
 )
 
 http_request_size_bytes = Histogram(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/NumberReplacer, occurrence: 20
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -31,7 +31,7 @@
     "stampbot_http_request_duration_seconds",
     "HTTP request duration in seconds",
     ["method", "endpoint"],
-    buckets=(0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 11.0),
 )
 
 http_request_size_bytes = Histogram(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/NumberReplacer, occurrence: 21
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -31,7 +31,7 @@
     "stampbot_http_request_duration_seconds",
     "HTTP request duration in seconds",
     ["method", "endpoint"],
-    buckets=(0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 9.0),
 )
 
 http_request_size_bytes = Histogram(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/NumberReplacer, occurrence: 22
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -38,7 +38,7 @@
     "stampbot_http_request_size_bytes",
     "HTTP request body size in bytes",
     ["method", "endpoint"],
-    buckets=(100, 500, 1000, 5000, 10000, 50000, 100000, 500000),
+    buckets=( 101, 500, 1000, 5000, 10000, 50000, 100000, 500000),
 )
 
 http_response_size_bytes = Histogram(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/NumberReplacer, occurrence: 23
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -38,7 +38,7 @@
     "stampbot_http_request_size_bytes",
     "HTTP request body size in bytes",
     ["method", "endpoint"],
-    buckets=(100, 500, 1000, 5000, 10000, 50000, 100000, 500000),
+    buckets=( 99, 500, 1000, 5000, 10000, 50000, 100000, 500000),
 )
 
 http_response_size_bytes = Histogram(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/NumberReplacer, occurrence: 24
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -38,7 +38,7 @@
     "stampbot_http_request_size_bytes",
     "HTTP request body size in bytes",
     ["method", "endpoint"],
-    buckets=(100, 500, 1000, 5000, 10000, 50000, 100000, 500000),
+    buckets=(100, 501, 1000, 5000, 10000, 50000, 100000, 500000),
 )
 
 http_response_size_bytes = Histogram(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/NumberReplacer, occurrence: 25
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -38,7 +38,7 @@
     "stampbot_http_request_size_bytes",
     "HTTP request body size in bytes",
     ["method", "endpoint"],
-    buckets=(100, 500, 1000, 5000, 10000, 50000, 100000, 500000),
+    buckets=(100, 499, 1000, 5000, 10000, 50000, 100000, 500000),
 )
 
 http_response_size_bytes = Histogram(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/NumberReplacer, occurrence: 26
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -38,7 +38,7 @@
     "stampbot_http_request_size_bytes",
     "HTTP request body size in bytes",
     ["method", "endpoint"],
-    buckets=(100, 500, 1000, 5000, 10000, 50000, 100000, 500000),
+    buckets=(100, 500, 1001, 5000, 10000, 50000, 100000, 500000),
 )
 
 http_response_size_bytes = Histogram(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/NumberReplacer, occurrence: 27
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -38,7 +38,7 @@
     "stampbot_http_request_size_bytes",
     "HTTP request body size in bytes",
     ["method", "endpoint"],
-    buckets=(100, 500, 1000, 5000, 10000, 50000, 100000, 500000),
+    buckets=(100, 500, 999, 5000, 10000, 50000, 100000, 500000),
 )
 
 http_response_size_bytes = Histogram(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/NumberReplacer, occurrence: 28
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -38,7 +38,7 @@
     "stampbot_http_request_size_bytes",
     "HTTP request body size in bytes",
     ["method", "endpoint"],
-    buckets=(100, 500, 1000, 5000, 10000, 50000, 100000, 500000),
+    buckets=(100, 500, 1000, 5001, 10000, 50000, 100000, 500000),
 )
 
 http_response_size_bytes = Histogram(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.66s
operator: core/NumberReplacer, occurrence: 29
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -38,7 +38,7 @@
     "stampbot_http_request_size_bytes",
     "HTTP request body size in bytes",
     ["method", "endpoint"],
-    buckets=(100, 500, 1000, 5000, 10000, 50000, 100000, 500000),
+    buckets=(100, 500, 1000, 4999, 10000, 50000, 100000, 500000),
 )
 
 http_response_size_bytes = Histogram(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/NumberReplacer, occurrence: 30
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -38,7 +38,7 @@
     "stampbot_http_request_size_bytes",
     "HTTP request body size in bytes",
     ["method", "endpoint"],
-    buckets=(100, 500, 1000, 5000, 10000, 50000, 100000, 500000),
+    buckets=(100, 500, 1000, 5000, 10001, 50000, 100000, 500000),
 )
 
 http_response_size_bytes = Histogram(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/NumberReplacer, occurrence: 31
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -38,7 +38,7 @@
     "stampbot_http_request_size_bytes",
     "HTTP request body size in bytes",
     ["method", "endpoint"],
-    buckets=(100, 500, 1000, 5000, 10000, 50000, 100000, 500000),
+    buckets=(100, 500, 1000, 5000, 9999, 50000, 100000, 500000),
 )
 
 http_response_size_bytes = Histogram(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.64s
operator: core/NumberReplacer, occurrence: 32
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -38,7 +38,7 @@
     "stampbot_http_request_size_bytes",
     "HTTP request body size in bytes",
     ["method", "endpoint"],
-    buckets=(100, 500, 1000, 5000, 10000, 50000, 100000, 500000),
+    buckets=(100, 500, 1000, 5000, 10000, 50001, 100000, 500000),
 )
 
 http_response_size_bytes = Histogram(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/NumberReplacer, occurrence: 33
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -38,7 +38,7 @@
     "stampbot_http_request_size_bytes",
     "HTTP request body size in bytes",
     ["method", "endpoint"],
-    buckets=(100, 500, 1000, 5000, 10000, 50000, 100000, 500000),
+    buckets=(100, 500, 1000, 5000, 10000, 49999, 100000, 500000),
 )
 
 http_response_size_bytes = Histogram(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/NumberReplacer, occurrence: 34
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -38,7 +38,7 @@
     "stampbot_http_request_size_bytes",
     "HTTP request body size in bytes",
     ["method", "endpoint"],
-    buckets=(100, 500, 1000, 5000, 10000, 50000, 100000, 500000),
+    buckets=(100, 500, 1000, 5000, 10000, 50000, 100001, 500000),
 )
 
 http_response_size_bytes = Histogram(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/NumberReplacer, occurrence: 35
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -38,7 +38,7 @@
     "stampbot_http_request_size_bytes",
     "HTTP request body size in bytes",
     ["method", "endpoint"],
-    buckets=(100, 500, 1000, 5000, 10000, 50000, 100000, 500000),
+    buckets=(100, 500, 1000, 5000, 10000, 50000, 99999, 500000),
 )
 
 http_response_size_bytes = Histogram(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/NumberReplacer, occurrence: 36
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -38,7 +38,7 @@
     "stampbot_http_request_size_bytes",
     "HTTP request body size in bytes",
     ["method", "endpoint"],
-    buckets=(100, 500, 1000, 5000, 10000, 50000, 100000, 500000),
+    buckets=(100, 500, 1000, 5000, 10000, 50000, 100000, 500001),
 )
 
 http_response_size_bytes = Histogram(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/NumberReplacer, occurrence: 37
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -38,7 +38,7 @@
     "stampbot_http_request_size_bytes",
     "HTTP request body size in bytes",
     ["method", "endpoint"],
-    buckets=(100, 500, 1000, 5000, 10000, 50000, 100000, 500000),
+    buckets=(100, 500, 1000, 5000, 10000, 50000, 100000, 499999),
 )
 
 http_response_size_bytes = Histogram(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/NumberReplacer, occurrence: 38
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -45,7 +45,7 @@
     "stampbot_http_response_size_bytes",
     "HTTP response body size in bytes",
     ["method", "endpoint"],
-    buckets=(100, 500, 1000, 5000, 10000, 50000, 100000, 500000),
+    buckets=( 101, 500, 1000, 5000, 10000, 50000, 100000, 500000),
 )
 
 http_requests_in_progress = Gauge(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.68s
operator: core/NumberReplacer, occurrence: 39
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -45,7 +45,7 @@
     "stampbot_http_response_size_bytes",
     "HTTP response body size in bytes",
     ["method", "endpoint"],
-    buckets=(100, 500, 1000, 5000, 10000, 50000, 100000, 500000),
+    buckets=( 99, 500, 1000, 5000, 10000, 50000, 100000, 500000),
 )
 
 http_requests_in_progress = Gauge(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/NumberReplacer, occurrence: 40
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -45,7 +45,7 @@
     "stampbot_http_response_size_bytes",
     "HTTP response body size in bytes",
     ["method", "endpoint"],
-    buckets=(100, 500, 1000, 5000, 10000, 50000, 100000, 500000),
+    buckets=(100, 501, 1000, 5000, 10000, 50000, 100000, 500000),
 )
 
 http_requests_in_progress = Gauge(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/NumberReplacer, occurrence: 41
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -45,7 +45,7 @@
     "stampbot_http_response_size_bytes",
     "HTTP response body size in bytes",
     ["method", "endpoint"],
-    buckets=(100, 500, 1000, 5000, 10000, 50000, 100000, 500000),
+    buckets=(100, 499, 1000, 5000, 10000, 50000, 100000, 500000),
 )
 
 http_requests_in_progress = Gauge(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/NumberReplacer, occurrence: 42
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -45,7 +45,7 @@
     "stampbot_http_response_size_bytes",
     "HTTP response body size in bytes",
     ["method", "endpoint"],
-    buckets=(100, 500, 1000, 5000, 10000, 50000, 100000, 500000),
+    buckets=(100, 500, 1001, 5000, 10000, 50000, 100000, 500000),
 )
 
 http_requests_in_progress = Gauge(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/NumberReplacer, occurrence: 43
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -45,7 +45,7 @@
     "stampbot_http_response_size_bytes",
     "HTTP response body size in bytes",
     ["method", "endpoint"],
-    buckets=(100, 500, 1000, 5000, 10000, 50000, 100000, 500000),
+    buckets=(100, 500, 999, 5000, 10000, 50000, 100000, 500000),
 )
 
 http_requests_in_progress = Gauge(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/NumberReplacer, occurrence: 44
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -45,7 +45,7 @@
     "stampbot_http_response_size_bytes",
     "HTTP response body size in bytes",
     ["method", "endpoint"],
-    buckets=(100, 500, 1000, 5000, 10000, 50000, 100000, 500000),
+    buckets=(100, 500, 1000, 5001, 10000, 50000, 100000, 500000),
 )
 
 http_requests_in_progress = Gauge(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/NumberReplacer, occurrence: 45
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -45,7 +45,7 @@
     "stampbot_http_response_size_bytes",
     "HTTP response body size in bytes",
     ["method", "endpoint"],
-    buckets=(100, 500, 1000, 5000, 10000, 50000, 100000, 500000),
+    buckets=(100, 500, 1000, 4999, 10000, 50000, 100000, 500000),
 )
 
 http_requests_in_progress = Gauge(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/NumberReplacer, occurrence: 46
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -45,7 +45,7 @@
     "stampbot_http_response_size_bytes",
     "HTTP response body size in bytes",
     ["method", "endpoint"],
-    buckets=(100, 500, 1000, 5000, 10000, 50000, 100000, 500000),
+    buckets=(100, 500, 1000, 5000, 10001, 50000, 100000, 500000),
 )
 
 http_requests_in_progress = Gauge(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/NumberReplacer, occurrence: 47
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -45,7 +45,7 @@
     "stampbot_http_response_size_bytes",
     "HTTP response body size in bytes",
     ["method", "endpoint"],
-    buckets=(100, 500, 1000, 5000, 10000, 50000, 100000, 500000),
+    buckets=(100, 500, 1000, 5000, 9999, 50000, 100000, 500000),
 )
 
 http_requests_in_progress = Gauge(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/NumberReplacer, occurrence: 48
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -45,7 +45,7 @@
     "stampbot_http_response_size_bytes",
     "HTTP response body size in bytes",
     ["method", "endpoint"],
-    buckets=(100, 500, 1000, 5000, 10000, 50000, 100000, 500000),
+    buckets=(100, 500, 1000, 5000, 10000, 50001, 100000, 500000),
 )
 
 http_requests_in_progress = Gauge(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/NumberReplacer, occurrence: 49
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -45,7 +45,7 @@
     "stampbot_http_response_size_bytes",
     "HTTP response body size in bytes",
     ["method", "endpoint"],
-    buckets=(100, 500, 1000, 5000, 10000, 50000, 100000, 500000),
+    buckets=(100, 500, 1000, 5000, 10000, 49999, 100000, 500000),
 )
 
 http_requests_in_progress = Gauge(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/NumberReplacer, occurrence: 50
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -45,7 +45,7 @@
     "stampbot_http_response_size_bytes",
     "HTTP response body size in bytes",
     ["method", "endpoint"],
-    buckets=(100, 500, 1000, 5000, 10000, 50000, 100000, 500000),
+    buckets=(100, 500, 1000, 5000, 10000, 50000, 100001, 500000),
 )
 
 http_requests_in_progress = Gauge(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/NumberReplacer, occurrence: 51
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -45,7 +45,7 @@
     "stampbot_http_response_size_bytes",
     "HTTP response body size in bytes",
     ["method", "endpoint"],
-    buckets=(100, 500, 1000, 5000, 10000, 50000, 100000, 500000),
+    buckets=(100, 500, 1000, 5000, 10000, 50000, 99999, 500000),
 )
 
 http_requests_in_progress = Gauge(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/NumberReplacer, occurrence: 52
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -45,7 +45,7 @@
     "stampbot_http_response_size_bytes",
     "HTTP response body size in bytes",
     ["method", "endpoint"],
-    buckets=(100, 500, 1000, 5000, 10000, 50000, 100000, 500000),
+    buckets=(100, 500, 1000, 5000, 10000, 50000, 100000, 500001),
 )
 
 http_requests_in_progress = Gauge(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/NumberReplacer, occurrence: 53
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -45,7 +45,7 @@
     "stampbot_http_response_size_bytes",
     "HTTP response body size in bytes",
     ["method", "endpoint"],
-    buckets=(100, 500, 1000, 5000, 10000, 50000, 100000, 500000),
+    buckets=(100, 500, 1000, 5000, 10000, 50000, 100000, 499999),
 )
 
 http_requests_in_progress = Gauge(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/NumberReplacer, occurrence: 54
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -74,7 +74,7 @@
     "stampbot_webhook_processing_duration_seconds",
     "Webhook event processing duration in seconds",
     ["event_type"],
-    buckets=(0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=( 1.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
 )
 
 # =============================================================================
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7f813491b9d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:73: in <module>
    webhook_processing_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7f8132df16e0>
source_buckets = (1.01, 0.025, 0.05, 0.1, 0.25, 0.5, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.66s
operator: core/NumberReplacer, occurrence: 55
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -74,7 +74,7 @@
     "stampbot_webhook_processing_duration_seconds",
     "Webhook event processing duration in seconds",
     ["event_type"],
-    buckets=(0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=( -0.99, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
 )
 
 # =============================================================================
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/NumberReplacer, occurrence: 56
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -74,7 +74,7 @@
     "stampbot_webhook_processing_duration_seconds",
     "Webhook event processing duration in seconds",
     ["event_type"],
-    buckets=(0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.01, 1.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
 )
 
 # =============================================================================
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7f19187979d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:73: in <module>
    webhook_processing_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7f1916bf16e0>
source_buckets = (0.01, 1.025, 0.05, 0.1, 0.25, 0.5, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.66s
operator: core/NumberReplacer, occurrence: 57
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -74,7 +74,7 @@
     "stampbot_webhook_processing_duration_seconds",
     "Webhook event processing duration in seconds",
     ["event_type"],
-    buckets=(0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.01, -0.975, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
 )
 
 # =============================================================================
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7f3b836739d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:73: in <module>
    webhook_processing_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7f3b818dd6e0>
source_buckets = (0.01, -0.975, 0.05, 0.1, 0.25, 0.5, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.67s
operator: core/NumberReplacer, occurrence: 58
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -74,7 +74,7 @@
     "stampbot_webhook_processing_duration_seconds",
     "Webhook event processing duration in seconds",
     ["event_type"],
-    buckets=(0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.01, 0.025, 1.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
 )
 
 # =============================================================================
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7f54842e39d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:73: in <module>
    webhook_processing_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7f54827f16e0>
source_buckets = (0.01, 0.025, 1.05, 0.1, 0.25, 0.5, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.67s
operator: core/NumberReplacer, occurrence: 59
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -74,7 +74,7 @@
     "stampbot_webhook_processing_duration_seconds",
     "Webhook event processing duration in seconds",
     ["event_type"],
-    buckets=(0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.01, 0.025, -0.95, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
 )
 
 # =============================================================================
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7f1d709c39d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:73: in <module>
    webhook_processing_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7f1d6edf16e0>
source_buckets = (0.01, 0.025, -0.95, 0.1, 0.25, 0.5, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.67s
operator: core/NumberReplacer, occurrence: 60
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -74,7 +74,7 @@
     "stampbot_webhook_processing_duration_seconds",
     "Webhook event processing duration in seconds",
     ["event_type"],
-    buckets=(0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.01, 0.025, 0.05, 1.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
 )
 
 # =============================================================================
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7fec260fb9d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:73: in <module>
    webhook_processing_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7fec244dd6e0>
source_buckets = (0.01, 0.025, 0.05, 1.1, 0.25, 0.5, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.66s
operator: core/NumberReplacer, occurrence: 61
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -74,7 +74,7 @@
     "stampbot_webhook_processing_duration_seconds",
     "Webhook event processing duration in seconds",
     ["event_type"],
-    buckets=(0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.01, 0.025, 0.05, -0.9, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
 )
 
 # =============================================================================
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7fcbfbeeb9d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:73: in <module>
    webhook_processing_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7fcbfa2dd6e0>
source_buckets = (0.01, 0.025, 0.05, -0.9, 0.25, 0.5, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.67s
operator: core/NumberReplacer, occurrence: 62
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -74,7 +74,7 @@
     "stampbot_webhook_processing_duration_seconds",
     "Webhook event processing duration in seconds",
     ["event_type"],
-    buckets=(0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.01, 0.025, 0.05, 0.1, 1.25, 0.5, 1.0, 2.5, 5.0, 10.0),
 )
 
 # =============================================================================
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7f4a675a39d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:73: in <module>
    webhook_processing_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7f4a659f16e0>
source_buckets = (0.01, 0.025, 0.05, 0.1, 1.25, 0.5, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.66s
operator: core/NumberReplacer, occurrence: 63
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -74,7 +74,7 @@
     "stampbot_webhook_processing_duration_seconds",
     "Webhook event processing duration in seconds",
     ["event_type"],
-    buckets=(0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.01, 0.025, 0.05, 0.1, -0.75, 0.5, 1.0, 2.5, 5.0, 10.0),
 )
 
 # =============================================================================
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7f06e3f379d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:73: in <module>
    webhook_processing_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7f06e23f16e0>
source_buckets = (0.01, 0.025, 0.05, 0.1, -0.75, 0.5, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.66s
operator: core/NumberReplacer, occurrence: 64
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -74,7 +74,7 @@
     "stampbot_webhook_processing_duration_seconds",
     "Webhook event processing duration in seconds",
     ["event_type"],
-    buckets=(0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.01, 0.025, 0.05, 0.1, 0.25, 1.5, 1.0, 2.5, 5.0, 10.0),
 )
 
 # =============================================================================
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7ffa961379d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:73: in <module>
    webhook_processing_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7ffa945d56e0>
source_buckets = (0.01, 0.025, 0.05, 0.1, 0.25, 1.5, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.66s
operator: core/NumberReplacer, occurrence: 65
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -74,7 +74,7 @@
     "stampbot_webhook_processing_duration_seconds",
     "Webhook event processing duration in seconds",
     ["event_type"],
-    buckets=(0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.01, 0.025, 0.05, 0.1, 0.25, -0.5, 1.0, 2.5, 5.0, 10.0),
 )
 
 # =============================================================================
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7f25e15879d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:73: in <module>
    webhook_processing_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7f25df9dd6e0>
source_buckets = (0.01, 0.025, 0.05, 0.1, 0.25, -0.5, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.66s
operator: core/NumberReplacer, occurrence: 66
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -74,7 +74,7 @@
     "stampbot_webhook_processing_duration_seconds",
     "Webhook event processing duration in seconds",
     ["event_type"],
-    buckets=(0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 2.0, 2.5, 5.0, 10.0),
 )
 
 # =============================================================================
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/NumberReplacer, occurrence: 67
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -74,7 +74,7 @@
     "stampbot_webhook_processing_duration_seconds",
     "Webhook event processing duration in seconds",
     ["event_type"],
-    buckets=(0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 0.0, 2.5, 5.0, 10.0),
 )
 
 # =============================================================================
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7f14902479d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:73: in <module>
    webhook_processing_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7f148e6f16e0>
source_buckets = (0.01, 0.025, 0.05, 0.1, 0.25, 0.5, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.67s
operator: core/NumberReplacer, occurrence: 68
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -74,7 +74,7 @@
     "stampbot_webhook_processing_duration_seconds",
     "Webhook event processing duration in seconds",
     ["event_type"],
-    buckets=(0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 3.5, 5.0, 10.0),
 )
 
 # =============================================================================
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/NumberReplacer, occurrence: 69
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -74,7 +74,7 @@
     "stampbot_webhook_processing_duration_seconds",
     "Webhook event processing duration in seconds",
     ["event_type"],
-    buckets=(0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 1.5, 5.0, 10.0),
 )
 
 # =============================================================================
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/NumberReplacer, occurrence: 70
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -74,7 +74,7 @@
     "stampbot_webhook_processing_duration_seconds",
     "Webhook event processing duration in seconds",
     ["event_type"],
-    buckets=(0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 6.0, 10.0),
 )
 
 # =============================================================================
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/NumberReplacer, occurrence: 71
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -74,7 +74,7 @@
     "stampbot_webhook_processing_duration_seconds",
     "Webhook event processing duration in seconds",
     ["event_type"],
-    buckets=(0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 4.0, 10.0),
 )
 
 # =============================================================================
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/NumberReplacer, occurrence: 72
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -74,7 +74,7 @@
     "stampbot_webhook_processing_duration_seconds",
     "Webhook event processing duration in seconds",
     ["event_type"],
-    buckets=(0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 11.0),
 )
 
 # =============================================================================
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/NumberReplacer, occurrence: 73
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -74,7 +74,7 @@
     "stampbot_webhook_processing_duration_seconds",
     "Webhook event processing duration in seconds",
     ["event_type"],
-    buckets=(0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 9.0),
 )
 
 # =============================================================================
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/NumberReplacer, occurrence: 74
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -90,7 +90,7 @@
 pr_approval_duration_seconds = Histogram(
     "stampbot_pr_approval_duration_seconds",
     "PR approval operation duration in seconds",
-    buckets=(0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=( 1.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
 )
 
 pr_dismissals_total = Counter(
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7fb7a004b9d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:90: in <module>
    pr_approval_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7fb79e4dd940>
source_buckets = (1.1, 0.25, 0.5, 1.0, 2.5, 5.0, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.67s
operator: core/NumberReplacer, occurrence: 75
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -90,7 +90,7 @@
 pr_approval_duration_seconds = Histogram(
     "stampbot_pr_approval_duration_seconds",
     "PR approval operation duration in seconds",
-    buckets=(0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=( -0.9, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
 )
 
 pr_dismissals_total = Counter(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.64s
operator: core/NumberReplacer, occurrence: 76
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -90,7 +90,7 @@
 pr_approval_duration_seconds = Histogram(
     "stampbot_pr_approval_duration_seconds",
     "PR approval operation duration in seconds",
-    buckets=(0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.1, 1.25, 0.5, 1.0, 2.5, 5.0, 10.0),
 )
 
 pr_dismissals_total = Counter(
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7fbc00c0f9d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:90: in <module>
    pr_approval_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7fbbff0dd940>
source_buckets = (0.1, 1.25, 0.5, 1.0, 2.5, 5.0, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.67s
operator: core/NumberReplacer, occurrence: 77
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -90,7 +90,7 @@
 pr_approval_duration_seconds = Histogram(
     "stampbot_pr_approval_duration_seconds",
     "PR approval operation duration in seconds",
-    buckets=(0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.1, -0.75, 0.5, 1.0, 2.5, 5.0, 10.0),
 )
 
 pr_dismissals_total = Counter(
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7f474934b9d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:90: in <module>
    pr_approval_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7f47477d9940>
source_buckets = (0.1, -0.75, 0.5, 1.0, 2.5, 5.0, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.65s
operator: core/NumberReplacer, occurrence: 78
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -90,7 +90,7 @@
 pr_approval_duration_seconds = Histogram(
     "stampbot_pr_approval_duration_seconds",
     "PR approval operation duration in seconds",
-    buckets=(0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.1, 0.25, 1.5, 1.0, 2.5, 5.0, 10.0),
 )
 
 pr_dismissals_total = Counter(
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7f6c267339d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:90: in <module>
    pr_approval_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7f6c24bdd940>
source_buckets = (0.1, 0.25, 1.5, 1.0, 2.5, 5.0, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.67s
operator: core/NumberReplacer, occurrence: 79
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -90,7 +90,7 @@
 pr_approval_duration_seconds = Histogram(
     "stampbot_pr_approval_duration_seconds",
     "PR approval operation duration in seconds",
-    buckets=(0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.1, 0.25, -0.5, 1.0, 2.5, 5.0, 10.0),
 )
 
 pr_dismissals_total = Counter(
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7f82d5ed39d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:90: in <module>
    pr_approval_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7f82d42dd940>
source_buckets = (0.1, 0.25, -0.5, 1.0, 2.5, 5.0, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.67s
operator: core/NumberReplacer, occurrence: 80
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -90,7 +90,7 @@
 pr_approval_duration_seconds = Histogram(
     "stampbot_pr_approval_duration_seconds",
     "PR approval operation duration in seconds",
-    buckets=(0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.1, 0.25, 0.5, 2.0, 2.5, 5.0, 10.0),
 )
 
 pr_dismissals_total = Counter(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/NumberReplacer, occurrence: 81
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -90,7 +90,7 @@
 pr_approval_duration_seconds = Histogram(
     "stampbot_pr_approval_duration_seconds",
     "PR approval operation duration in seconds",
-    buckets=(0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.1, 0.25, 0.5, 0.0, 2.5, 5.0, 10.0),
 )
 
 pr_dismissals_total = Counter(
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7ff8c3ce39d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:90: in <module>
    pr_approval_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7ff8c21f1940>
source_buckets = (0.1, 0.25, 0.5, 0.0, 2.5, 5.0, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.67s
operator: core/NumberReplacer, occurrence: 82
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -90,7 +90,7 @@
 pr_approval_duration_seconds = Histogram(
     "stampbot_pr_approval_duration_seconds",
     "PR approval operation duration in seconds",
-    buckets=(0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.1, 0.25, 0.5, 1.0, 3.5, 5.0, 10.0),
 )
 
 pr_dismissals_total = Counter(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/NumberReplacer, occurrence: 83
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -90,7 +90,7 @@
 pr_approval_duration_seconds = Histogram(
     "stampbot_pr_approval_duration_seconds",
     "PR approval operation duration in seconds",
-    buckets=(0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.1, 0.25, 0.5, 1.0, 1.5, 5.0, 10.0),
 )
 
 pr_dismissals_total = Counter(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/NumberReplacer, occurrence: 84
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -90,7 +90,7 @@
 pr_approval_duration_seconds = Histogram(
     "stampbot_pr_approval_duration_seconds",
     "PR approval operation duration in seconds",
-    buckets=(0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.1, 0.25, 0.5, 1.0, 2.5, 6.0, 10.0),
 )
 
 pr_dismissals_total = Counter(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/NumberReplacer, occurrence: 85
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -90,7 +90,7 @@
 pr_approval_duration_seconds = Histogram(
     "stampbot_pr_approval_duration_seconds",
     "PR approval operation duration in seconds",
-    buckets=(0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.1, 0.25, 0.5, 1.0, 2.5, 4.0, 10.0),
 )
 
 pr_dismissals_total = Counter(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/NumberReplacer, occurrence: 86
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -90,7 +90,7 @@
 pr_approval_duration_seconds = Histogram(
     "stampbot_pr_approval_duration_seconds",
     "PR approval operation duration in seconds",
-    buckets=(0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 11.0),
 )
 
 pr_dismissals_total = Counter(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/NumberReplacer, occurrence: 87
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -90,7 +90,7 @@
 pr_approval_duration_seconds = Histogram(
     "stampbot_pr_approval_duration_seconds",
     "PR approval operation duration in seconds",
-    buckets=(0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 9.0),
 )
 
 pr_dismissals_total = Counter(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/NumberReplacer, occurrence: 88
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -102,7 +102,7 @@
 pr_dismissal_duration_seconds = Histogram(
     "stampbot_pr_dismissal_duration_seconds",
     "PR dismissal operation duration in seconds",
-    buckets=(0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=( 1.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
 )
 
 # =============================================================================
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7f78baed79d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:102: in <module>
    pr_dismissal_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7f78b9119370>
source_buckets = (1.1, 0.25, 0.5, 1.0, 2.5, 5.0, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.67s
operator: core/NumberReplacer, occurrence: 89
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -102,7 +102,7 @@
 pr_dismissal_duration_seconds = Histogram(
     "stampbot_pr_dismissal_duration_seconds",
     "PR dismissal operation duration in seconds",
-    buckets=(0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=( -0.9, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
 )
 
 # =============================================================================
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/NumberReplacer, occurrence: 90
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -102,7 +102,7 @@
 pr_dismissal_duration_seconds = Histogram(
     "stampbot_pr_dismissal_duration_seconds",
     "PR dismissal operation duration in seconds",
-    buckets=(0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.1, 1.25, 0.5, 1.0, 2.5, 5.0, 10.0),
 )
 
 # =============================================================================
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7f2b2d5c79d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:102: in <module>
    pr_dismissal_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7f2b2bab1370>
source_buckets = (0.1, 1.25, 0.5, 1.0, 2.5, 5.0, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.67s
operator: core/NumberReplacer, occurrence: 91
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -102,7 +102,7 @@
 pr_dismissal_duration_seconds = Histogram(
     "stampbot_pr_dismissal_duration_seconds",
     "PR dismissal operation duration in seconds",
-    buckets=(0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.1, -0.75, 0.5, 1.0, 2.5, 5.0, 10.0),
 )
 
 # =============================================================================
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7f53db7cb9d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:102: in <module>
    pr_dismissal_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7f53d9a2d370>
source_buckets = (0.1, -0.75, 0.5, 1.0, 2.5, 5.0, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.67s
operator: core/NumberReplacer, occurrence: 92
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -102,7 +102,7 @@
 pr_dismissal_duration_seconds = Histogram(
     "stampbot_pr_dismissal_duration_seconds",
     "PR dismissal operation duration in seconds",
-    buckets=(0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.1, 0.25, 1.5, 1.0, 2.5, 5.0, 10.0),
 )
 
 # =============================================================================
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7f88179cf9d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:102: in <module>
    pr_dismissal_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7f8815c29370>
source_buckets = (0.1, 0.25, 1.5, 1.0, 2.5, 5.0, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.66s
operator: core/NumberReplacer, occurrence: 93
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -102,7 +102,7 @@
 pr_dismissal_duration_seconds = Histogram(
     "stampbot_pr_dismissal_duration_seconds",
     "PR dismissal operation duration in seconds",
-    buckets=(0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.1, 0.25, -0.5, 1.0, 2.5, 5.0, 10.0),
 )
 
 # =============================================================================
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7f9cb77479d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:102: in <module>
    pr_dismissal_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7f9cb5a2d370>
source_buckets = (0.1, 0.25, -0.5, 1.0, 2.5, 5.0, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.67s
operator: core/NumberReplacer, occurrence: 94
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -102,7 +102,7 @@
 pr_dismissal_duration_seconds = Histogram(
     "stampbot_pr_dismissal_duration_seconds",
     "PR dismissal operation duration in seconds",
-    buckets=(0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.1, 0.25, 0.5, 2.0, 2.5, 5.0, 10.0),
 )
 
 # =============================================================================
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/NumberReplacer, occurrence: 95
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -102,7 +102,7 @@
 pr_dismissal_duration_seconds = Histogram(
     "stampbot_pr_dismissal_duration_seconds",
     "PR dismissal operation duration in seconds",
-    buckets=(0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.1, 0.25, 0.5, 0.0, 2.5, 5.0, 10.0),
 )
 
 # =============================================================================
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7fdb29f0b9d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:102: in <module>
    pr_dismissal_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7fdb28229370>
source_buckets = (0.1, 0.25, 0.5, 0.0, 2.5, 5.0, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.66s
operator: core/NumberReplacer, occurrence: 96
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -102,7 +102,7 @@
 pr_dismissal_duration_seconds = Histogram(
     "stampbot_pr_dismissal_duration_seconds",
     "PR dismissal operation duration in seconds",
-    buckets=(0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.1, 0.25, 0.5, 1.0, 3.5, 5.0, 10.0),
 )
 
 # =============================================================================
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/NumberReplacer, occurrence: 97
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -102,7 +102,7 @@
 pr_dismissal_duration_seconds = Histogram(
     "stampbot_pr_dismissal_duration_seconds",
     "PR dismissal operation duration in seconds",
-    buckets=(0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.1, 0.25, 0.5, 1.0, 1.5, 5.0, 10.0),
 )
 
 # =============================================================================
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/NumberReplacer, occurrence: 98
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -102,7 +102,7 @@
 pr_dismissal_duration_seconds = Histogram(
     "stampbot_pr_dismissal_duration_seconds",
     "PR dismissal operation duration in seconds",
-    buckets=(0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.1, 0.25, 0.5, 1.0, 2.5, 6.0, 10.0),
 )
 
 # =============================================================================
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/NumberReplacer, occurrence: 99
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -102,7 +102,7 @@
 pr_dismissal_duration_seconds = Histogram(
     "stampbot_pr_dismissal_duration_seconds",
     "PR dismissal operation duration in seconds",
-    buckets=(0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.1, 0.25, 0.5, 1.0, 2.5, 4.0, 10.0),
 )
 
 # =============================================================================
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/NumberReplacer, occurrence: 100
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -102,7 +102,7 @@
 pr_dismissal_duration_seconds = Histogram(
     "stampbot_pr_dismissal_duration_seconds",
     "PR dismissal operation duration in seconds",
-    buckets=(0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 11.0),
 )
 
 # =============================================================================
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/NumberReplacer, occurrence: 101
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -102,7 +102,7 @@
 pr_dismissal_duration_seconds = Histogram(
     "stampbot_pr_dismissal_duration_seconds",
     "PR dismissal operation duration in seconds",
-    buckets=(0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0),
+    buckets=(0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 9.0),
 )
 
 # =============================================================================
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/NumberReplacer, occurrence: 102
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -132,7 +132,7 @@
     "stampbot_github_api_request_duration_seconds",
     "GitHub API request duration in seconds",
     ["operation"],
-    buckets=(0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, 30.0),
+    buckets=( 1.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, 30.0),
 )
 
 github_api_rate_limit_remaining = Gauge(
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7fabf578f9d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:131: in <module>
    github_api_request_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7fabf3b49040>
source_buckets = (1.05, 0.1, 0.25, 0.5, 1.0, 2.5, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.67s
operator: core/NumberReplacer, occurrence: 103
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -132,7 +132,7 @@
     "stampbot_github_api_request_duration_seconds",
     "GitHub API request duration in seconds",
     ["operation"],
-    buckets=(0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, 30.0),
+    buckets=( -0.95, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, 30.0),
 )
 
 github_api_rate_limit_remaining = Gauge(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/NumberReplacer, occurrence: 104
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -132,7 +132,7 @@
     "stampbot_github_api_request_duration_seconds",
     "GitHub API request duration in seconds",
     ["operation"],
-    buckets=(0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, 30.0),
+    buckets=(0.05, 1.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, 30.0),
 )
 
 github_api_rate_limit_remaining = Gauge(
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7fafb9a8b9d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:131: in <module>
    github_api_request_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7fafb7d39040>
source_buckets = (0.05, 1.1, 0.25, 0.5, 1.0, 2.5, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.67s
operator: core/NumberReplacer, occurrence: 105
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -132,7 +132,7 @@
     "stampbot_github_api_request_duration_seconds",
     "GitHub API request duration in seconds",
     ["operation"],
-    buckets=(0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, 30.0),
+    buckets=(0.05, -0.9, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, 30.0),
 )
 
 github_api_rate_limit_remaining = Gauge(
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7f6469e579d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:131: in <module>
    github_api_request_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7f6468155040>
source_buckets = (0.05, -0.9, 0.25, 0.5, 1.0, 2.5, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.66s
operator: core/NumberReplacer, occurrence: 106
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -132,7 +132,7 @@
     "stampbot_github_api_request_duration_seconds",
     "GitHub API request duration in seconds",
     ["operation"],
-    buckets=(0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, 30.0),
+    buckets=(0.05, 0.1, 1.25, 0.5, 1.0, 2.5, 5.0, 10.0, 30.0),
 )
 
 github_api_rate_limit_remaining = Gauge(
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7ff27bcdb9d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:131: in <module>
    github_api_request_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7ff27a049040>
source_buckets = (0.05, 0.1, 1.25, 0.5, 1.0, 2.5, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.66s
operator: core/NumberReplacer, occurrence: 107
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -132,7 +132,7 @@
     "stampbot_github_api_request_duration_seconds",
     "GitHub API request duration in seconds",
     ["operation"],
-    buckets=(0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, 30.0),
+    buckets=(0.05, 0.1, -0.75, 0.5, 1.0, 2.5, 5.0, 10.0, 30.0),
 )
 
 github_api_rate_limit_remaining = Gauge(
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7f59de90b9d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:131: in <module>
    github_api_request_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7f59dcc49040>
source_buckets = (0.05, 0.1, -0.75, 0.5, 1.0, 2.5, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.67s
operator: core/NumberReplacer, occurrence: 108
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -132,7 +132,7 @@
     "stampbot_github_api_request_duration_seconds",
     "GitHub API request duration in seconds",
     ["operation"],
-    buckets=(0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, 30.0),
+    buckets=(0.05, 0.1, 0.25, 1.5, 1.0, 2.5, 5.0, 10.0, 30.0),
 )
 
 github_api_rate_limit_remaining = Gauge(
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7f89c68579d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:131: in <module>
    github_api_request_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7f89c4b51040>
source_buckets = (0.05, 0.1, 0.25, 1.5, 1.0, 2.5, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.66s
operator: core/NumberReplacer, occurrence: 109
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -132,7 +132,7 @@
     "stampbot_github_api_request_duration_seconds",
     "GitHub API request duration in seconds",
     ["operation"],
-    buckets=(0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, 30.0),
+    buckets=(0.05, 0.1, 0.25, -0.5, 1.0, 2.5, 5.0, 10.0, 30.0),
 )
 
 github_api_rate_limit_remaining = Gauge(
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7f391f82f9d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:131: in <module>
    github_api_request_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7f391db55040>
source_buckets = (0.05, 0.1, 0.25, -0.5, 1.0, 2.5, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.66s
operator: core/NumberReplacer, occurrence: 110
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -132,7 +132,7 @@
     "stampbot_github_api_request_duration_seconds",
     "GitHub API request duration in seconds",
     ["operation"],
-    buckets=(0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, 30.0),
+    buckets=(0.05, 0.1, 0.25, 0.5, 2.0, 2.5, 5.0, 10.0, 30.0),
 )
 
 github_api_rate_limit_remaining = Gauge(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/NumberReplacer, occurrence: 111
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -132,7 +132,7 @@
     "stampbot_github_api_request_duration_seconds",
     "GitHub API request duration in seconds",
     ["operation"],
-    buckets=(0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, 30.0),
+    buckets=(0.05, 0.1, 0.25, 0.5, 0.0, 2.5, 5.0, 10.0, 30.0),
 )
 
 github_api_rate_limit_remaining = Gauge(
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7f7b4b5db9d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
>       from stampbot.github_client import GitHubAppClient

tests/test_github_client.py:19: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:15: in <module>
    from stampbot.metrics import (
stampbot/metrics.py:131: in <module>
    github_api_request_duration_seconds = Histogram(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:625: in __init__
    self._prepare_buckets(buckets)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Histogram' object has no attribute '_name'") raised in repr()] Histogram object at 0x7f7b49871040>
source_buckets = (0.05, 0.1, 0.25, 0.5, 0.0, 2.5, ...)

    def _prepare_buckets(self, source_buckets: Sequence[Union[float, str]]) -> None:
        buckets = [float(b) for b in source_buckets]
        if buckets != sorted(buckets):
            # This is probably an error on the part of the user,
            # so raise rather than sorting for them.
>           raise ValueError('Buckets not in sorted order')
E           ValueError: Buckets not in sorted order

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/prometheus_client/metrics.py:643: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - ValueError: Buckets not in sorted order
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.66s
operator: core/NumberReplacer, occurrence: 112
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -132,7 +132,7 @@
     "stampbot_github_api_request_duration_seconds",
     "GitHub API request duration in seconds",
     ["operation"],
-    buckets=(0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, 30.0),
+    buckets=(0.05, 0.1, 0.25, 0.5, 1.0, 3.5, 5.0, 10.0, 30.0),
 )
 
 github_api_rate_limit_remaining = Gauge(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/NumberReplacer, occurrence: 113
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -132,7 +132,7 @@
     "stampbot_github_api_request_duration_seconds",
     "GitHub API request duration in seconds",
     ["operation"],
-    buckets=(0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, 30.0),
+    buckets=(0.05, 0.1, 0.25, 0.5, 1.0, 1.5, 5.0, 10.0, 30.0),
 )
 
 github_api_rate_limit_remaining = Gauge(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/NumberReplacer, occurrence: 114
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -132,7 +132,7 @@
     "stampbot_github_api_request_duration_seconds",
     "GitHub API request duration in seconds",
     ["operation"],
-    buckets=(0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, 30.0),
+    buckets=(0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 6.0, 10.0, 30.0),
 )
 
 github_api_rate_limit_remaining = Gauge(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/NumberReplacer, occurrence: 115
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -132,7 +132,7 @@
     "stampbot_github_api_request_duration_seconds",
     "GitHub API request duration in seconds",
     ["operation"],
-    buckets=(0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, 30.0),
+    buckets=(0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 4.0, 10.0, 30.0),
 )
 
 github_api_rate_limit_remaining = Gauge(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/NumberReplacer, occurrence: 116
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -132,7 +132,7 @@
     "stampbot_github_api_request_duration_seconds",
     "GitHub API request duration in seconds",
     ["operation"],
-    buckets=(0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, 30.0),
+    buckets=(0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 11.0, 30.0),
 )
 
 github_api_rate_limit_remaining = Gauge(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/NumberReplacer, occurrence: 117
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -132,7 +132,7 @@
     "stampbot_github_api_request_duration_seconds",
     "GitHub API request duration in seconds",
     ["operation"],
-    buckets=(0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, 30.0),
+    buckets=(0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 9.0, 30.0),
 )
 
 github_api_rate_limit_remaining = Gauge(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/NumberReplacer, occurrence: 118
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -132,7 +132,7 @@
     "stampbot_github_api_request_duration_seconds",
     "GitHub API request duration in seconds",
     ["operation"],
-    buckets=(0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, 30.0),
+    buckets=(0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, 31.0),
 )
 
 github_api_rate_limit_remaining = Gauge(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/NumberReplacer, occurrence: 119
--- mutation diff ---
--- astampbot/metrics.py
+++ bstampbot/metrics.py
@@ -132,7 +132,7 @@
     "stampbot_github_api_request_duration_seconds",
     "GitHub API request duration in seconds",
     ["operation"],
-    buckets=(0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, 30.0),
+    buckets=(0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, 29.0),
 )
 
 github_api_rate_limit_remaining = Gauge(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceBinaryOperator_Pow_Add, occurrence: 0
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -251,7 +251,7 @@
             repo_settings = toml.loads(toml_content)
 
         # Merge: repo settings override defaults
-        merged = {**defaults, **repo_settings}
+        merged = {+defaults, **repo_settings}
 
         import re
 
==================================== ERRORS ====================================
____________________ ERROR collecting tests/test_config.py _____________________
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/python.py:507: in importtestmodule
    mod = import_path(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/pathlib.py:587: in import_path
    importlib.import_module(module_name)
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/importlib/__init__.py:88: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
<frozen importlib._bootstrap>:1398: in _gcd_import
    ???
<frozen importlib._bootstrap>:1371: in _find_and_load
    ???
<frozen importlib._bootstrap>:1342: in _find_and_load_unlocked
    ???
<frozen importlib._bootstrap>:938: in _load_unlocked
    ???
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/assertion/rewrite.py:197: in exec_module
    exec(co, module.__dict__)
tests/test_config.py:7: in <module>
    from stampbot.config import RepoConfig, get_setting, is_configured
E     File "/home/runner/work/stampbot/stampbot/stampbot/config.py", line 254
E       merged = {+defaults, **repo_settings}
E                            ^^
E   SyntaxError: invalid syntax
=========================== short test summary info ============================
ERROR tests/test_config.py
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 error in 0.14s
operator: core/ReplaceBinaryOperator_Pow_Add, occurrence: 1
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -251,7 +251,7 @@
             repo_settings = toml.loads(toml_content)
 
         # Merge: repo settings override defaults
-        merged = {**defaults, **repo_settings}
+        merged = {**defaults, +repo_settings}
 
         import re
 
==================================== ERRORS ====================================
____________________ ERROR collecting tests/test_config.py _____________________
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/python.py:507: in importtestmodule
    mod = import_path(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/pathlib.py:587: in import_path
    importlib.import_module(module_name)
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/importlib/__init__.py:88: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
<frozen importlib._bootstrap>:1398: in _gcd_import
    ???
<frozen importlib._bootstrap>:1371: in _find_and_load
    ???
<frozen importlib._bootstrap>:1342: in _find_and_load_unlocked
    ???
<frozen importlib._bootstrap>:938: in _load_unlocked
    ???
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/assertion/rewrite.py:197: in exec_module
    exec(co, module.__dict__)
tests/test_config.py:7: in <module>
    from stampbot.config import RepoConfig, get_setting, is_configured
E     File "/home/runner/work/stampbot/stampbot/stampbot/config.py", line 254
E       merged = {**defaults, +repo_settings}
E                                          ^
E   SyntaxError: ':' expected after dictionary key
=========================== short test summary info ============================
ERROR tests/test_config.py
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 error in 0.13s
operator: core/ReplaceBinaryOperator_Pow_Sub, occurrence: 0
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -251,7 +251,7 @@
             repo_settings = toml.loads(toml_content)
 
         # Merge: repo settings override defaults
-        merged = {**defaults, **repo_settings}
+        merged = {-defaults, **repo_settings}
 
         import re
 
==================================== ERRORS ====================================
____________________ ERROR collecting tests/test_config.py _____________________
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/python.py:507: in importtestmodule
    mod = import_path(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/pathlib.py:587: in import_path
    importlib.import_module(module_name)
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/importlib/__init__.py:88: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
<frozen importlib._bootstrap>:1398: in _gcd_import
    ???
<frozen importlib._bootstrap>:1371: in _find_and_load
    ???
<frozen importlib._bootstrap>:1342: in _find_and_load_unlocked
    ???
<frozen importlib._bootstrap>:938: in _load_unlocked
    ???
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/assertion/rewrite.py:197: in exec_module
    exec(co, module.__dict__)
tests/test_config.py:7: in <module>
    from stampbot.config import RepoConfig, get_setting, is_configured
E     File "/home/runner/work/stampbot/stampbot/stampbot/config.py", line 254
E       merged = {-defaults, **repo_settings}
E                            ^^
E   SyntaxError: invalid syntax
=========================== short test summary info ============================
ERROR tests/test_config.py
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 error in 0.14s
operator: core/ReplaceBinaryOperator_Pow_Sub, occurrence: 1
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -251,7 +251,7 @@
             repo_settings = toml.loads(toml_content)
 
         # Merge: repo settings override defaults
-        merged = {**defaults, **repo_settings}
+        merged = {**defaults, -repo_settings}
 
         import re
 
==================================== ERRORS ====================================
____________________ ERROR collecting tests/test_config.py _____________________
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/python.py:507: in importtestmodule
    mod = import_path(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/pathlib.py:587: in import_path
    importlib.import_module(module_name)
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/importlib/__init__.py:88: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
<frozen importlib._bootstrap>:1398: in _gcd_import
    ???
<frozen importlib._bootstrap>:1371: in _find_and_load
    ???
<frozen importlib._bootstrap>:1342: in _find_and_load_unlocked
    ???
<frozen importlib._bootstrap>:938: in _load_unlocked
    ???
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/assertion/rewrite.py:197: in exec_module
    exec(co, module.__dict__)
tests/test_config.py:7: in <module>
    from stampbot.config import RepoConfig, get_setting, is_configured
E     File "/home/runner/work/stampbot/stampbot/stampbot/config.py", line 254
E       merged = {**defaults, -repo_settings}
E                                          ^
E   SyntaxError: ':' expected after dictionary key
=========================== short test summary info ============================
ERROR tests/test_config.py
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 error in 0.13s
operator: core/ReplaceBinaryOperator_Pow_Mul, occurrence: 0
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -251,7 +251,7 @@
             repo_settings = toml.loads(toml_content)
 
         # Merge: repo settings override defaults
-        merged = {**defaults, **repo_settings}
+        merged = {*defaults, **repo_settings}
 
         import re
 
==================================== ERRORS ====================================
____________________ ERROR collecting tests/test_config.py _____________________
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/python.py:507: in importtestmodule
    mod = import_path(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/pathlib.py:587: in import_path
    importlib.import_module(module_name)
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/importlib/__init__.py:88: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
<frozen importlib._bootstrap>:1398: in _gcd_import
    ???
<frozen importlib._bootstrap>:1371: in _find_and_load
    ???
<frozen importlib._bootstrap>:1342: in _find_and_load_unlocked
    ???
<frozen importlib._bootstrap>:938: in _load_unlocked
    ???
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/assertion/rewrite.py:197: in exec_module
    exec(co, module.__dict__)
tests/test_config.py:7: in <module>
    from stampbot.config import RepoConfig, get_setting, is_configured
E     File "/home/runner/work/stampbot/stampbot/stampbot/config.py", line 254
E       merged = {*defaults, **repo_settings}
E                            ^^
E   SyntaxError: invalid syntax
=========================== short test summary info ============================
ERROR tests/test_config.py
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 error in 0.14s
operator: core/ReplaceBinaryOperator_Pow_Mul, occurrence: 1
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -251,7 +251,7 @@
             repo_settings = toml.loads(toml_content)
 
         # Merge: repo settings override defaults
-        merged = {**defaults, **repo_settings}
+        merged = {**defaults, *repo_settings}
 
         import re
 
==================================== ERRORS ====================================
____________________ ERROR collecting tests/test_config.py _____________________
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/python.py:507: in importtestmodule
    mod = import_path(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/pathlib.py:587: in import_path
    importlib.import_module(module_name)
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/importlib/__init__.py:88: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
<frozen importlib._bootstrap>:1398: in _gcd_import
    ???
<frozen importlib._bootstrap>:1371: in _find_and_load
    ???
<frozen importlib._bootstrap>:1342: in _find_and_load_unlocked
    ???
<frozen importlib._bootstrap>:938: in _load_unlocked
    ???
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/assertion/rewrite.py:197: in exec_module
    exec(co, module.__dict__)
tests/test_config.py:7: in <module>
    from stampbot.config import RepoConfig, get_setting, is_configured
E     File "/home/runner/work/stampbot/stampbot/stampbot/config.py", line 254
E       merged = {**defaults, *repo_settings}
E                             ^
E   SyntaxError: invalid syntax
=========================== short test summary info ============================
ERROR tests/test_config.py
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 error in 0.14s
operator: core/ReplaceBinaryOperator_Pow_Div, occurrence: 0
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -251,7 +251,7 @@
             repo_settings = toml.loads(toml_content)
 
         # Merge: repo settings override defaults
-        merged = {**defaults, **repo_settings}
+        merged = {/defaults, **repo_settings}
 
         import re
 
==================================== ERRORS ====================================
____________________ ERROR collecting tests/test_config.py _____________________
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/python.py:507: in importtestmodule
    mod = import_path(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/pathlib.py:587: in import_path
    importlib.import_module(module_name)
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/importlib/__init__.py:88: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
<frozen importlib._bootstrap>:1398: in _gcd_import
    ???
<frozen importlib._bootstrap>:1371: in _find_and_load
    ???
<frozen importlib._bootstrap>:1342: in _find_and_load_unlocked
    ???
<frozen importlib._bootstrap>:938: in _load_unlocked
    ???
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/assertion/rewrite.py:197: in exec_module
    exec(co, module.__dict__)
tests/test_config.py:7: in <module>
    from stampbot.config import RepoConfig, get_setting, is_configured
E     File "/home/runner/work/stampbot/stampbot/stampbot/config.py", line 254
E       merged = {/defaults, **repo_settings}
E                 ^
E   SyntaxError: invalid syntax
=========================== short test summary info ============================
ERROR tests/test_config.py
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 error in 0.14s
operator: core/ReplaceBinaryOperator_Pow_Div, occurrence: 1
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -251,7 +251,7 @@
             repo_settings = toml.loads(toml_content)
 
         # Merge: repo settings override defaults
-        merged = {**defaults, **repo_settings}
+        merged = {**defaults, /repo_settings}
 
         import re
 
==================================== ERRORS ====================================
____________________ ERROR collecting tests/test_config.py _____________________
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/python.py:507: in importtestmodule
    mod = import_path(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/pathlib.py:587: in import_path
    importlib.import_module(module_name)
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/importlib/__init__.py:88: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
<frozen importlib._bootstrap>:1398: in _gcd_import
    ???
<frozen importlib._bootstrap>:1371: in _find_and_load
    ???
<frozen importlib._bootstrap>:1342: in _find_and_load_unlocked
    ???
<frozen importlib._bootstrap>:938: in _load_unlocked
    ???
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/assertion/rewrite.py:197: in exec_module
    exec(co, module.__dict__)
tests/test_config.py:7: in <module>
    from stampbot.config import RepoConfig, get_setting, is_configured
E     File "/home/runner/work/stampbot/stampbot/stampbot/config.py", line 254
E       merged = {**defaults, /repo_settings}
E                             ^
E   SyntaxError: invalid syntax
=========================== short test summary info ============================
ERROR tests/test_config.py
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 error in 0.14s
operator: core/ReplaceBinaryOperator_Pow_FloorDiv, occurrence: 0
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -251,7 +251,7 @@
             repo_settings = toml.loads(toml_content)
 
         # Merge: repo settings override defaults
-        merged = {**defaults, **repo_settings}
+        merged = {//defaults, **repo_settings}
 
         import re
 
==================================== ERRORS ====================================
____________________ ERROR collecting tests/test_config.py _____________________
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/python.py:507: in importtestmodule
    mod = import_path(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/pathlib.py:587: in import_path
    importlib.import_module(module_name)
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/importlib/__init__.py:88: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
<frozen importlib._bootstrap>:1398: in _gcd_import
    ???
<frozen importlib._bootstrap>:1371: in _find_and_load
    ???
<frozen importlib._bootstrap>:1342: in _find_and_load_unlocked
    ???
<frozen importlib._bootstrap>:938: in _load_unlocked
    ???
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/assertion/rewrite.py:197: in exec_module
    exec(co, module.__dict__)
tests/test_config.py:7: in <module>
    from stampbot.config import RepoConfig, get_setting, is_configured
E     File "/home/runner/work/stampbot/stampbot/stampbot/config.py", line 254
E       merged = {//defaults, **repo_settings}
E                 ^^
E   SyntaxError: invalid syntax
=========================== short test summary info ============================
ERROR tests/test_config.py
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 error in 0.14s
operator: core/ReplaceBinaryOperator_Pow_FloorDiv, occurrence: 1
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -251,7 +251,7 @@
             repo_settings = toml.loads(toml_content)
 
         # Merge: repo settings override defaults
-        merged = {**defaults, **repo_settings}
+        merged = {**defaults, //repo_settings}
 
         import re
 
==================================== ERRORS ====================================
____________________ ERROR collecting tests/test_config.py _____________________
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/python.py:507: in importtestmodule
    mod = import_path(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/pathlib.py:587: in import_path
    importlib.import_module(module_name)
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/importlib/__init__.py:88: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
<frozen importlib._bootstrap>:1398: in _gcd_import
    ???
<frozen importlib._bootstrap>:1371: in _find_and_load
    ???
<frozen importlib._bootstrap>:1342: in _find_and_load_unlocked
    ???
<frozen importlib._bootstrap>:938: in _load_unlocked
    ???
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/assertion/rewrite.py:197: in exec_module
    exec(co, module.__dict__)
tests/test_config.py:7: in <module>
    from stampbot.config import RepoConfig, get_setting, is_configured
E     File "/home/runner/work/stampbot/stampbot/stampbot/config.py", line 254
E       merged = {**defaults, //repo_settings}
E                             ^^
E   SyntaxError: invalid syntax
=========================== short test summary info ============================
ERROR tests/test_config.py
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 error in 0.14s
operator: core/ReplaceBinaryOperator_Pow_Mod, occurrence: 0
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -251,7 +251,7 @@
             repo_settings = toml.loads(toml_content)
 
         # Merge: repo settings override defaults
-        merged = {**defaults, **repo_settings}
+        merged = {%defaults, **repo_settings}
 
         import re
 
==================================== ERRORS ====================================
____________________ ERROR collecting tests/test_config.py _____________________
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/python.py:507: in importtestmodule
    mod = import_path(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/pathlib.py:587: in import_path
    importlib.import_module(module_name)
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/importlib/__init__.py:88: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
<frozen importlib._bootstrap>:1398: in _gcd_import
    ???
<frozen importlib._bootstrap>:1371: in _find_and_load
    ???
<frozen importlib._bootstrap>:1342: in _find_and_load_unlocked
    ???
<frozen importlib._bootstrap>:938: in _load_unlocked
    ???
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/assertion/rewrite.py:197: in exec_module
    exec(co, module.__dict__)
tests/test_config.py:7: in <module>
    from stampbot.config import RepoConfig, get_setting, is_configured
E     File "/home/runner/work/stampbot/stampbot/stampbot/config.py", line 254
E       merged = {%defaults, **repo_settings}
E                 ^
E   SyntaxError: invalid syntax
=========================== short test summary info ============================
ERROR tests/test_config.py
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 error in 0.14s
operator: core/ReplaceBinaryOperator_Pow_Mod, occurrence: 1
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -251,7 +251,7 @@
             repo_settings = toml.loads(toml_content)
 
         # Merge: repo settings override defaults
-        merged = {**defaults, **repo_settings}
+        merged = {**defaults, %repo_settings}
 
         import re
 
==================================== ERRORS ====================================
____________________ ERROR collecting tests/test_config.py _____________________
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/python.py:507: in importtestmodule
    mod = import_path(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/pathlib.py:587: in import_path
    importlib.import_module(module_name)
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/importlib/__init__.py:88: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
<frozen importlib._bootstrap>:1398: in _gcd_import
    ???
<frozen importlib._bootstrap>:1371: in _find_and_load
    ???
<frozen importlib._bootstrap>:1342: in _find_and_load_unlocked
    ???
<frozen importlib._bootstrap>:938: in _load_unlocked
    ???
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/assertion/rewrite.py:197: in exec_module
    exec(co, module.__dict__)
tests/test_config.py:7: in <module>
    from stampbot.config import RepoConfig, get_setting, is_configured
E     File "/home/runner/work/stampbot/stampbot/stampbot/config.py", line 254
E       merged = {**defaults, %repo_settings}
E                             ^
E   SyntaxError: invalid syntax
=========================== short test summary info ============================
ERROR tests/test_config.py
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 error in 0.14s
operator: core/ReplaceBinaryOperator_Pow_RShift, occurrence: 0
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -251,7 +251,7 @@
             repo_settings = toml.loads(toml_content)
 
         # Merge: repo settings override defaults
-        merged = {**defaults, **repo_settings}
+        merged = {>>defaults, **repo_settings}
 
         import re
 
==================================== ERRORS ====================================
____________________ ERROR collecting tests/test_config.py _____________________
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/python.py:507: in importtestmodule
    mod = import_path(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/pathlib.py:587: in import_path
    importlib.import_module(module_name)
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/importlib/__init__.py:88: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
<frozen importlib._bootstrap>:1398: in _gcd_import
    ???
<frozen importlib._bootstrap>:1371: in _find_and_load
    ???
<frozen importlib._bootstrap>:1342: in _find_and_load_unlocked
    ???
<frozen importlib._bootstrap>:938: in _load_unlocked
    ???
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/assertion/rewrite.py:197: in exec_module
    exec(co, module.__dict__)
tests/test_config.py:7: in <module>
    from stampbot.config import RepoConfig, get_setting, is_configured
E     File "/home/runner/work/stampbot/stampbot/stampbot/config.py", line 254
E       merged = {>>defaults, **repo_settings}
E                 ^^
E   SyntaxError: invalid syntax
=========================== short test summary info ============================
ERROR tests/test_config.py
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 error in 0.14s
operator: core/ReplaceBinaryOperator_Pow_RShift, occurrence: 1
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -251,7 +251,7 @@
             repo_settings = toml.loads(toml_content)
 
         # Merge: repo settings override defaults
-        merged = {**defaults, **repo_settings}
+        merged = {**defaults, >>repo_settings}
 
         import re
 
==================================== ERRORS ====================================
____________________ ERROR collecting tests/test_config.py _____________________
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/python.py:507: in importtestmodule
    mod = import_path(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/pathlib.py:587: in import_path
    importlib.import_module(module_name)
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/importlib/__init__.py:88: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
<frozen importlib._bootstrap>:1398: in _gcd_import
    ???
<frozen importlib._bootstrap>:1371: in _find_and_load
    ???
<frozen importlib._bootstrap>:1342: in _find_and_load_unlocked
    ???
<frozen importlib._bootstrap>:938: in _load_unlocked
    ???
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/assertion/rewrite.py:197: in exec_module
    exec(co, module.__dict__)
tests/test_config.py:7: in <module>
    from stampbot.config import RepoConfig, get_setting, is_configured
E     File "/home/runner/work/stampbot/stampbot/stampbot/config.py", line 254
E       merged = {**defaults, >>repo_settings}
E                             ^^
E   SyntaxError: invalid syntax
=========================== short test summary info ============================
ERROR tests/test_config.py
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 error in 0.14s
operator: core/ReplaceBinaryOperator_Pow_LShift, occurrence: 0
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -251,7 +251,7 @@
             repo_settings = toml.loads(toml_content)
 
         # Merge: repo settings override defaults
-        merged = {**defaults, **repo_settings}
+        merged = {<<defaults, **repo_settings}
 
         import re
 
==================================== ERRORS ====================================
____________________ ERROR collecting tests/test_config.py _____________________
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/python.py:507: in importtestmodule
    mod = import_path(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/pathlib.py:587: in import_path
    importlib.import_module(module_name)
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/importlib/__init__.py:88: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
<frozen importlib._bootstrap>:1398: in _gcd_import
    ???
<frozen importlib._bootstrap>:1371: in _find_and_load
    ???
<frozen importlib._bootstrap>:1342: in _find_and_load_unlocked
    ???
<frozen importlib._bootstrap>:938: in _load_unlocked
    ???
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/assertion/rewrite.py:197: in exec_module
    exec(co, module.__dict__)
tests/test_config.py:7: in <module>
    from stampbot.config import RepoConfig, get_setting, is_configured
E     File "/home/runner/work/stampbot/stampbot/stampbot/config.py", line 254
E       merged = {<<defaults, **repo_settings}
E                 ^^
E   SyntaxError: invalid syntax
=========================== short test summary info ============================
ERROR tests/test_config.py
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 error in 0.14s
operator: core/ReplaceBinaryOperator_Pow_LShift, occurrence: 1
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -251,7 +251,7 @@
             repo_settings = toml.loads(toml_content)
 
         # Merge: repo settings override defaults
-        merged = {**defaults, **repo_settings}
+        merged = {**defaults, <<repo_settings}
 
         import re
 
==================================== ERRORS ====================================
____________________ ERROR collecting tests/test_config.py _____________________
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/python.py:507: in importtestmodule
    mod = import_path(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/pathlib.py:587: in import_path
    importlib.import_module(module_name)
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/importlib/__init__.py:88: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
<frozen importlib._bootstrap>:1398: in _gcd_import
    ???
<frozen importlib._bootstrap>:1371: in _find_and_load
    ???
<frozen importlib._bootstrap>:1342: in _find_and_load_unlocked
    ???
<frozen importlib._bootstrap>:938: in _load_unlocked
    ???
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/assertion/rewrite.py:197: in exec_module
    exec(co, module.__dict__)
tests/test_config.py:7: in <module>
    from stampbot.config import RepoConfig, get_setting, is_configured
E     File "/home/runner/work/stampbot/stampbot/stampbot/config.py", line 254
E       merged = {**defaults, <<repo_settings}
E                             ^^
E   SyntaxError: invalid syntax
=========================== short test summary info ============================
ERROR tests/test_config.py
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 error in 0.14s
operator: core/ReplaceBinaryOperator_Pow_BitOr, occurrence: 0
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -251,7 +251,7 @@
             repo_settings = toml.loads(toml_content)
 
         # Merge: repo settings override defaults
-        merged = {**defaults, **repo_settings}
+        merged = {|defaults, **repo_settings}
 
         import re
 
==================================== ERRORS ====================================
____________________ ERROR collecting tests/test_config.py _____________________
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/python.py:507: in importtestmodule
    mod = import_path(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/pathlib.py:587: in import_path
    importlib.import_module(module_name)
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/importlib/__init__.py:88: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
<frozen importlib._bootstrap>:1398: in _gcd_import
    ???
<frozen importlib._bootstrap>:1371: in _find_and_load
    ???
<frozen importlib._bootstrap>:1342: in _find_and_load_unlocked
    ???
<frozen importlib._bootstrap>:938: in _load_unlocked
    ???
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/assertion/rewrite.py:197: in exec_module
    exec(co, module.__dict__)
tests/test_config.py:7: in <module>
    from stampbot.config import RepoConfig, get_setting, is_configured
E     File "/home/runner/work/stampbot/stampbot/stampbot/config.py", line 254
E       merged = {|defaults, **repo_settings}
E                 ^
E   SyntaxError: invalid syntax
=========================== short test summary info ============================
ERROR tests/test_config.py
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 error in 0.14s
operator: core/ReplaceBinaryOperator_Pow_BitOr, occurrence: 1
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -251,7 +251,7 @@
             repo_settings = toml.loads(toml_content)
 
         # Merge: repo settings override defaults
-        merged = {**defaults, **repo_settings}
+        merged = {**defaults, |repo_settings}
 
         import re
 
==================================== ERRORS ====================================
____________________ ERROR collecting tests/test_config.py _____________________
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/python.py:507: in importtestmodule
    mod = import_path(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/pathlib.py:587: in import_path
    importlib.import_module(module_name)
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/importlib/__init__.py:88: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
<frozen importlib._bootstrap>:1398: in _gcd_import
    ???
<frozen importlib._bootstrap>:1371: in _find_and_load
    ???
<frozen importlib._bootstrap>:1342: in _find_and_load_unlocked
    ???
<frozen importlib._bootstrap>:938: in _load_unlocked
    ???
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/assertion/rewrite.py:197: in exec_module
    exec(co, module.__dict__)
tests/test_config.py:7: in <module>
    from stampbot.config import RepoConfig, get_setting, is_configured
E     File "/home/runner/work/stampbot/stampbot/stampbot/config.py", line 254
E       merged = {**defaults, |repo_settings}
E                             ^
E   SyntaxError: invalid syntax
=========================== short test summary info ============================
ERROR tests/test_config.py
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 error in 0.14s
operator: core/ReplaceBinaryOperator_Pow_BitAnd, occurrence: 0
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -251,7 +251,7 @@
             repo_settings = toml.loads(toml_content)
 
         # Merge: repo settings override defaults
-        merged = {**defaults, **repo_settings}
+        merged = {&defaults, **repo_settings}
 
         import re
 
==================================== ERRORS ====================================
____________________ ERROR collecting tests/test_config.py _____________________
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/python.py:507: in importtestmodule
    mod = import_path(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/pathlib.py:587: in import_path
    importlib.import_module(module_name)
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/importlib/__init__.py:88: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
<frozen importlib._bootstrap>:1398: in _gcd_import
    ???
<frozen importlib._bootstrap>:1371: in _find_and_load
    ???
<frozen importlib._bootstrap>:1342: in _find_and_load_unlocked
    ???
<frozen importlib._bootstrap>:938: in _load_unlocked
    ???
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/assertion/rewrite.py:197: in exec_module
    exec(co, module.__dict__)
tests/test_config.py:7: in <module>
    from stampbot.config import RepoConfig, get_setting, is_configured
E     File "/home/runner/work/stampbot/stampbot/stampbot/config.py", line 254
E       merged = {&defaults, **repo_settings}
E                 ^
E   SyntaxError: invalid syntax
=========================== short test summary info ============================
ERROR tests/test_config.py
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 error in 0.14s
operator: core/ReplaceBinaryOperator_Pow_BitAnd, occurrence: 1
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -251,7 +251,7 @@
             repo_settings = toml.loads(toml_content)
 
         # Merge: repo settings override defaults
-        merged = {**defaults, **repo_settings}
+        merged = {**defaults, &repo_settings}
 
         import re
 
==================================== ERRORS ====================================
____________________ ERROR collecting tests/test_config.py _____________________
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/python.py:507: in importtestmodule
    mod = import_path(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/pathlib.py:587: in import_path
    importlib.import_module(module_name)
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/importlib/__init__.py:88: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
<frozen importlib._bootstrap>:1398: in _gcd_import
    ???
<frozen importlib._bootstrap>:1371: in _find_and_load
    ???
<frozen importlib._bootstrap>:1342: in _find_and_load_unlocked
    ???
<frozen importlib._bootstrap>:938: in _load_unlocked
    ???
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/assertion/rewrite.py:197: in exec_module
    exec(co, module.__dict__)
tests/test_config.py:7: in <module>
    from stampbot.config import RepoConfig, get_setting, is_configured
E     File "/home/runner/work/stampbot/stampbot/stampbot/config.py", line 254
E       merged = {**defaults, &repo_settings}
E                             ^
E   SyntaxError: invalid syntax
=========================== short test summary info ============================
ERROR tests/test_config.py
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 error in 0.14s
operator: core/ReplaceBinaryOperator_Pow_BitXor, occurrence: 0
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -251,7 +251,7 @@
             repo_settings = toml.loads(toml_content)
 
         # Merge: repo settings override defaults
-        merged = {**defaults, **repo_settings}
+        merged = {^defaults, **repo_settings}
 
         import re
 
==================================== ERRORS ====================================
____________________ ERROR collecting tests/test_config.py _____________________
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/python.py:507: in importtestmodule
    mod = import_path(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/pathlib.py:587: in import_path
    importlib.import_module(module_name)
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/importlib/__init__.py:88: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
<frozen importlib._bootstrap>:1398: in _gcd_import
    ???
<frozen importlib._bootstrap>:1371: in _find_and_load
    ???
<frozen importlib._bootstrap>:1342: in _find_and_load_unlocked
    ???
<frozen importlib._bootstrap>:938: in _load_unlocked
    ???
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/assertion/rewrite.py:197: in exec_module
    exec(co, module.__dict__)
tests/test_config.py:7: in <module>
    from stampbot.config import RepoConfig, get_setting, is_configured
E     File "/home/runner/work/stampbot/stampbot/stampbot/config.py", line 254
E       merged = {^defaults, **repo_settings}
E                 ^
E   SyntaxError: invalid syntax
=========================== short test summary info ============================
ERROR tests/test_config.py
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 error in 0.14s
operator: core/ReplaceBinaryOperator_Pow_BitXor, occurrence: 1
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -251,7 +251,7 @@
             repo_settings = toml.loads(toml_content)
 
         # Merge: repo settings override defaults
-        merged = {**defaults, **repo_settings}
+        merged = {**defaults, ^repo_settings}
 
         import re
 
==================================== ERRORS ====================================
____________________ ERROR collecting tests/test_config.py _____________________
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/python.py:507: in importtestmodule
    mod = import_path(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/pathlib.py:587: in import_path
    importlib.import_module(module_name)
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/importlib/__init__.py:88: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
<frozen importlib._bootstrap>:1398: in _gcd_import
    ???
<frozen importlib._bootstrap>:1371: in _find_and_load
    ???
<frozen importlib._bootstrap>:1342: in _find_and_load_unlocked
    ???
<frozen importlib._bootstrap>:938: in _load_unlocked
    ???
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/assertion/rewrite.py:197: in exec_module
    exec(co, module.__dict__)
tests/test_config.py:7: in <module>
    from stampbot.config import RepoConfig, get_setting, is_configured
E     File "/home/runner/work/stampbot/stampbot/stampbot/config.py", line 254
E       merged = {**defaults, ^repo_settings}
E                             ^
E   SyntaxError: invalid syntax
=========================== short test summary info ============================
ERROR tests/test_config.py
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 error in 0.20s
operator: core/ReplaceBinaryOperator_BitOr_Add, occurrence: 0
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -86,7 +86,7 @@
         chatops_required_permission: str,
         approve_commands: list[str],
         unapprove_commands: list[str],
-        required_labels: list[str] | None = None,
+        required_labels: list[str] + None = None,
         required_title_patterns: list[str] | None = None,
         allowed_users: list[str] | None = None,
         allowed_teams: list[str] | None = None,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_Add, occurrence: 1
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -87,7 +87,7 @@
         approve_commands: list[str],
         unapprove_commands: list[str],
         required_labels: list[str] | None = None,
-        required_title_patterns: list[str] | None = None,
+        required_title_patterns: list[str] + None = None,
         allowed_users: list[str] | None = None,
         allowed_teams: list[str] | None = None,
         config_error: str | None = None,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceBinaryOperator_BitOr_Add, occurrence: 2
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -88,7 +88,7 @@
         unapprove_commands: list[str],
         required_labels: list[str] | None = None,
         required_title_patterns: list[str] | None = None,
-        allowed_users: list[str] | None = None,
+        allowed_users: list[str] + None = None,
         allowed_teams: list[str] | None = None,
         config_error: str | None = None,
     ):
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_Add, occurrence: 3
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -89,7 +89,7 @@
         required_labels: list[str] | None = None,
         required_title_patterns: list[str] | None = None,
         allowed_users: list[str] | None = None,
-        allowed_teams: list[str] | None = None,
+        allowed_teams: list[str] + None = None,
         config_error: str | None = None,
     ):
         """Initialize repo configuration.
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceBinaryOperator_BitOr_Add, occurrence: 4
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -90,7 +90,7 @@
         required_title_patterns: list[str] | None = None,
         allowed_users: list[str] | None = None,
         allowed_teams: list[str] | None = None,
-        config_error: str | None = None,
+        config_error: str + None = None,
     ):
         """Initialize repo configuration.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_Add, occurrence: 5
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -136,7 +136,7 @@
         pr_labels: list[str],
         pr_title: str,
         pr_author: str,
-        author_team_slugs: list[str] | None = None,
+        author_team_slugs: list[str] + None = None,
     ) -> tuple[bool, str | None]:
         """Check if a PR is eligible for auto-approval based on filters.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Add, occurrence: 6
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -137,7 +137,7 @@
         pr_title: str,
         pr_author: str,
         author_team_slugs: list[str] | None = None,
-    ) -> tuple[bool, str | None]:
+    ) -> tuple[bool, str + None]:
         """Check if a PR is eligible for auto-approval based on filters.
 
         All configured filters must pass (AND logic between filter types).
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.67s
operator: core/ReplaceBinaryOperator_BitOr_Sub, occurrence: 0
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -86,7 +86,7 @@
         chatops_required_permission: str,
         approve_commands: list[str],
         unapprove_commands: list[str],
-        required_labels: list[str] | None = None,
+        required_labels: list[str] - None = None,
         required_title_patterns: list[str] | None = None,
         allowed_users: list[str] | None = None,
         allowed_teams: list[str] | None = None,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Sub, occurrence: 1
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -87,7 +87,7 @@
         approve_commands: list[str],
         unapprove_commands: list[str],
         required_labels: list[str] | None = None,
-        required_title_patterns: list[str] | None = None,
+        required_title_patterns: list[str] - None = None,
         allowed_users: list[str] | None = None,
         allowed_teams: list[str] | None = None,
         config_error: str | None = None,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_Sub, occurrence: 2
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -88,7 +88,7 @@
         unapprove_commands: list[str],
         required_labels: list[str] | None = None,
         required_title_patterns: list[str] | None = None,
-        allowed_users: list[str] | None = None,
+        allowed_users: list[str] - None = None,
         allowed_teams: list[str] | None = None,
         config_error: str | None = None,
     ):
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Sub, occurrence: 3
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -89,7 +89,7 @@
         required_labels: list[str] | None = None,
         required_title_patterns: list[str] | None = None,
         allowed_users: list[str] | None = None,
-        allowed_teams: list[str] | None = None,
+        allowed_teams: list[str] - None = None,
         config_error: str | None = None,
     ):
         """Initialize repo configuration.
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceBinaryOperator_BitOr_Sub, occurrence: 4
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -90,7 +90,7 @@
         required_title_patterns: list[str] | None = None,
         allowed_users: list[str] | None = None,
         allowed_teams: list[str] | None = None,
-        config_error: str | None = None,
+        config_error: str - None = None,
     ):
         """Initialize repo configuration.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_Sub, occurrence: 5
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -136,7 +136,7 @@
         pr_labels: list[str],
         pr_title: str,
         pr_author: str,
-        author_team_slugs: list[str] | None = None,
+        author_team_slugs: list[str] - None = None,
     ) -> tuple[bool, str | None]:
         """Check if a PR is eligible for auto-approval based on filters.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Sub, occurrence: 6
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -137,7 +137,7 @@
         pr_title: str,
         pr_author: str,
         author_team_slugs: list[str] | None = None,
-    ) -> tuple[bool, str | None]:
+    ) -> tuple[bool, str - None]:
         """Check if a PR is eligible for auto-approval based on filters.
 
         All configured filters must pass (AND logic between filter types).
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Mul, occurrence: 0
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -86,7 +86,7 @@
         chatops_required_permission: str,
         approve_commands: list[str],
         unapprove_commands: list[str],
-        required_labels: list[str] | None = None,
+        required_labels: list[str] * None = None,
         required_title_patterns: list[str] | None = None,
         allowed_users: list[str] | None = None,
         allowed_teams: list[str] | None = None,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Mul, occurrence: 1
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -87,7 +87,7 @@
         approve_commands: list[str],
         unapprove_commands: list[str],
         required_labels: list[str] | None = None,
-        required_title_patterns: list[str] | None = None,
+        required_title_patterns: list[str] * None = None,
         allowed_users: list[str] | None = None,
         allowed_teams: list[str] | None = None,
         config_error: str | None = None,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Mul, occurrence: 2
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -88,7 +88,7 @@
         unapprove_commands: list[str],
         required_labels: list[str] | None = None,
         required_title_patterns: list[str] | None = None,
-        allowed_users: list[str] | None = None,
+        allowed_users: list[str] * None = None,
         allowed_teams: list[str] | None = None,
         config_error: str | None = None,
     ):
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.64s
operator: core/ReplaceBinaryOperator_BitOr_Mul, occurrence: 3
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -89,7 +89,7 @@
         required_labels: list[str] | None = None,
         required_title_patterns: list[str] | None = None,
         allowed_users: list[str] | None = None,
-        allowed_teams: list[str] | None = None,
+        allowed_teams: list[str] * None = None,
         config_error: str | None = None,
     ):
         """Initialize repo configuration.
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_Mul, occurrence: 4
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -90,7 +90,7 @@
         required_title_patterns: list[str] | None = None,
         allowed_users: list[str] | None = None,
         allowed_teams: list[str] | None = None,
-        config_error: str | None = None,
+        config_error: str * None = None,
     ):
         """Initialize repo configuration.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_Mul, occurrence: 5
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -136,7 +136,7 @@
         pr_labels: list[str],
         pr_title: str,
         pr_author: str,
-        author_team_slugs: list[str] | None = None,
+        author_team_slugs: list[str] * None = None,
     ) -> tuple[bool, str | None]:
         """Check if a PR is eligible for auto-approval based on filters.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_Mul, occurrence: 6
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -137,7 +137,7 @@
         pr_title: str,
         pr_author: str,
         author_team_slugs: list[str] | None = None,
-    ) -> tuple[bool, str | None]:
+    ) -> tuple[bool, str * None]:
         """Check if a PR is eligible for auto-approval based on filters.
 
         All configured filters must pass (AND logic between filter types).
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Div, occurrence: 0
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -86,7 +86,7 @@
         chatops_required_permission: str,
         approve_commands: list[str],
         unapprove_commands: list[str],
-        required_labels: list[str] | None = None,
+        required_labels: list[str] / None = None,
         required_title_patterns: list[str] | None = None,
         allowed_users: list[str] | None = None,
         allowed_teams: list[str] | None = None,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceBinaryOperator_BitOr_Div, occurrence: 1
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -87,7 +87,7 @@
         approve_commands: list[str],
         unapprove_commands: list[str],
         required_labels: list[str] | None = None,
-        required_title_patterns: list[str] | None = None,
+        required_title_patterns: list[str] / None = None,
         allowed_users: list[str] | None = None,
         allowed_teams: list[str] | None = None,
         config_error: str | None = None,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_Div, occurrence: 2
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -88,7 +88,7 @@
         unapprove_commands: list[str],
         required_labels: list[str] | None = None,
         required_title_patterns: list[str] | None = None,
-        allowed_users: list[str] | None = None,
+        allowed_users: list[str] / None = None,
         allowed_teams: list[str] | None = None,
         config_error: str | None = None,
     ):
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_Div, occurrence: 3
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -89,7 +89,7 @@
         required_labels: list[str] | None = None,
         required_title_patterns: list[str] | None = None,
         allowed_users: list[str] | None = None,
-        allowed_teams: list[str] | None = None,
+        allowed_teams: list[str] / None = None,
         config_error: str | None = None,
     ):
         """Initialize repo configuration.
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Div, occurrence: 4
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -90,7 +90,7 @@
         required_title_patterns: list[str] | None = None,
         allowed_users: list[str] | None = None,
         allowed_teams: list[str] | None = None,
-        config_error: str | None = None,
+        config_error: str / None = None,
     ):
         """Initialize repo configuration.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Div, occurrence: 5
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -136,7 +136,7 @@
         pr_labels: list[str],
         pr_title: str,
         pr_author: str,
-        author_team_slugs: list[str] | None = None,
+        author_team_slugs: list[str] / None = None,
     ) -> tuple[bool, str | None]:
         """Check if a PR is eligible for auto-approval based on filters.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Div, occurrence: 6
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -137,7 +137,7 @@
         pr_title: str,
         pr_author: str,
         author_team_slugs: list[str] | None = None,
-    ) -> tuple[bool, str | None]:
+    ) -> tuple[bool, str / None]:
         """Check if a PR is eligible for auto-approval based on filters.
 
         All configured filters must pass (AND logic between filter types).
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.59s
operator: core/ReplaceBinaryOperator_BitOr_FloorDiv, occurrence: 0
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -86,7 +86,7 @@
         chatops_required_permission: str,
         approve_commands: list[str],
         unapprove_commands: list[str],
-        required_labels: list[str] | None = None,
+        required_labels: list[str] // None = None,
         required_title_patterns: list[str] | None = None,
         allowed_users: list[str] | None = None,
         allowed_teams: list[str] | None = None,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_FloorDiv, occurrence: 1
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -87,7 +87,7 @@
         approve_commands: list[str],
         unapprove_commands: list[str],
         required_labels: list[str] | None = None,
-        required_title_patterns: list[str] | None = None,
+        required_title_patterns: list[str] // None = None,
         allowed_users: list[str] | None = None,
         allowed_teams: list[str] | None = None,
         config_error: str | None = None,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_FloorDiv, occurrence: 2
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -88,7 +88,7 @@
         unapprove_commands: list[str],
         required_labels: list[str] | None = None,
         required_title_patterns: list[str] | None = None,
-        allowed_users: list[str] | None = None,
+        allowed_users: list[str] // None = None,
         allowed_teams: list[str] | None = None,
         config_error: str | None = None,
     ):
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_FloorDiv, occurrence: 3
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -89,7 +89,7 @@
         required_labels: list[str] | None = None,
         required_title_patterns: list[str] | None = None,
         allowed_users: list[str] | None = None,
-        allowed_teams: list[str] | None = None,
+        allowed_teams: list[str] // None = None,
         config_error: str | None = None,
     ):
         """Initialize repo configuration.
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_FloorDiv, occurrence: 4
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -90,7 +90,7 @@
         required_title_patterns: list[str] | None = None,
         allowed_users: list[str] | None = None,
         allowed_teams: list[str] | None = None,
-        config_error: str | None = None,
+        config_error: str // None = None,
     ):
         """Initialize repo configuration.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceBinaryOperator_BitOr_FloorDiv, occurrence: 5
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -136,7 +136,7 @@
         pr_labels: list[str],
         pr_title: str,
         pr_author: str,
-        author_team_slugs: list[str] | None = None,
+        author_team_slugs: list[str] // None = None,
     ) -> tuple[bool, str | None]:
         """Check if a PR is eligible for auto-approval based on filters.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_FloorDiv, occurrence: 6
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -137,7 +137,7 @@
         pr_title: str,
         pr_author: str,
         author_team_slugs: list[str] | None = None,
-    ) -> tuple[bool, str | None]:
+    ) -> tuple[bool, str // None]:
         """Check if a PR is eligible for auto-approval based on filters.
 
         All configured filters must pass (AND logic between filter types).
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Mod, occurrence: 0
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -86,7 +86,7 @@
         chatops_required_permission: str,
         approve_commands: list[str],
         unapprove_commands: list[str],
-        required_labels: list[str] | None = None,
+        required_labels: list[str] % None = None,
         required_title_patterns: list[str] | None = None,
         allowed_users: list[str] | None = None,
         allowed_teams: list[str] | None = None,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_Mod, occurrence: 1
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -87,7 +87,7 @@
         approve_commands: list[str],
         unapprove_commands: list[str],
         required_labels: list[str] | None = None,
-        required_title_patterns: list[str] | None = None,
+        required_title_patterns: list[str] % None = None,
         allowed_users: list[str] | None = None,
         allowed_teams: list[str] | None = None,
         config_error: str | None = None,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Mod, occurrence: 2
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -88,7 +88,7 @@
         unapprove_commands: list[str],
         required_labels: list[str] | None = None,
         required_title_patterns: list[str] | None = None,
-        allowed_users: list[str] | None = None,
+        allowed_users: list[str] % None = None,
         allowed_teams: list[str] | None = None,
         config_error: str | None = None,
     ):
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Mod, occurrence: 3
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -89,7 +89,7 @@
         required_labels: list[str] | None = None,
         required_title_patterns: list[str] | None = None,
         allowed_users: list[str] | None = None,
-        allowed_teams: list[str] | None = None,
+        allowed_teams: list[str] % None = None,
         config_error: str | None = None,
     ):
         """Initialize repo configuration.
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceBinaryOperator_BitOr_Mod, occurrence: 4
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -90,7 +90,7 @@
         required_title_patterns: list[str] | None = None,
         allowed_users: list[str] | None = None,
         allowed_teams: list[str] | None = None,
-        config_error: str | None = None,
+        config_error: str % None = None,
     ):
         """Initialize repo configuration.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceBinaryOperator_BitOr_Mod, occurrence: 5
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -136,7 +136,7 @@
         pr_labels: list[str],
         pr_title: str,
         pr_author: str,
-        author_team_slugs: list[str] | None = None,
+        author_team_slugs: list[str] % None = None,
     ) -> tuple[bool, str | None]:
         """Check if a PR is eligible for auto-approval based on filters.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/ReplaceBinaryOperator_BitOr_Mod, occurrence: 6
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -137,7 +137,7 @@
         pr_title: str,
         pr_author: str,
         author_team_slugs: list[str] | None = None,
-    ) -> tuple[bool, str | None]:
+    ) -> tuple[bool, str % None]:
         """Check if a PR is eligible for auto-approval based on filters.
 
         All configured filters must pass (AND logic between filter types).
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_Pow, occurrence: 0
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -86,7 +86,7 @@
         chatops_required_permission: str,
         approve_commands: list[str],
         unapprove_commands: list[str],
-        required_labels: list[str] | None = None,
+        required_labels: list[str] ** None = None,
         required_title_patterns: list[str] | None = None,
         allowed_users: list[str] | None = None,
         allowed_teams: list[str] | None = None,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/ReplaceBinaryOperator_BitOr_Pow, occurrence: 1
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -87,7 +87,7 @@
         approve_commands: list[str],
         unapprove_commands: list[str],
         required_labels: list[str] | None = None,
-        required_title_patterns: list[str] | None = None,
+        required_title_patterns: list[str] ** None = None,
         allowed_users: list[str] | None = None,
         allowed_teams: list[str] | None = None,
         config_error: str | None = None,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_Pow, occurrence: 2
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -88,7 +88,7 @@
         unapprove_commands: list[str],
         required_labels: list[str] | None = None,
         required_title_patterns: list[str] | None = None,
-        allowed_users: list[str] | None = None,
+        allowed_users: list[str] ** None = None,
         allowed_teams: list[str] | None = None,
         config_error: str | None = None,
     ):
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_Pow, occurrence: 3
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -89,7 +89,7 @@
         required_labels: list[str] | None = None,
         required_title_patterns: list[str] | None = None,
         allowed_users: list[str] | None = None,
-        allowed_teams: list[str] | None = None,
+        allowed_teams: list[str] ** None = None,
         config_error: str | None = None,
     ):
         """Initialize repo configuration.
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceBinaryOperator_BitOr_Pow, occurrence: 4
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -90,7 +90,7 @@
         required_title_patterns: list[str] | None = None,
         allowed_users: list[str] | None = None,
         allowed_teams: list[str] | None = None,
-        config_error: str | None = None,
+        config_error: str ** None = None,
     ):
         """Initialize repo configuration.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_Pow, occurrence: 5
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -136,7 +136,7 @@
         pr_labels: list[str],
         pr_title: str,
         pr_author: str,
-        author_team_slugs: list[str] | None = None,
+        author_team_slugs: list[str] ** None = None,
     ) -> tuple[bool, str | None]:
         """Check if a PR is eligible for auto-approval based on filters.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_Pow, occurrence: 6
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -137,7 +137,7 @@
         pr_title: str,
         pr_author: str,
         author_team_slugs: list[str] | None = None,
-    ) -> tuple[bool, str | None]:
+    ) -> tuple[bool, str ** None]:
         """Check if a PR is eligible for auto-approval based on filters.
 
         All configured filters must pass (AND logic between filter types).
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceBinaryOperator_BitOr_RShift, occurrence: 0
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -86,7 +86,7 @@
         chatops_required_permission: str,
         approve_commands: list[str],
         unapprove_commands: list[str],
-        required_labels: list[str] | None = None,
+        required_labels: list[str] >> None = None,
         required_title_patterns: list[str] | None = None,
         allowed_users: list[str] | None = None,
         allowed_teams: list[str] | None = None,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_RShift, occurrence: 1
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -87,7 +87,7 @@
         approve_commands: list[str],
         unapprove_commands: list[str],
         required_labels: list[str] | None = None,
-        required_title_patterns: list[str] | None = None,
+        required_title_patterns: list[str] >> None = None,
         allowed_users: list[str] | None = None,
         allowed_teams: list[str] | None = None,
         config_error: str | None = None,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceBinaryOperator_BitOr_RShift, occurrence: 2
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -88,7 +88,7 @@
         unapprove_commands: list[str],
         required_labels: list[str] | None = None,
         required_title_patterns: list[str] | None = None,
-        allowed_users: list[str] | None = None,
+        allowed_users: list[str] >> None = None,
         allowed_teams: list[str] | None = None,
         config_error: str | None = None,
     ):
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_RShift, occurrence: 3
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -89,7 +89,7 @@
         required_labels: list[str] | None = None,
         required_title_patterns: list[str] | None = None,
         allowed_users: list[str] | None = None,
-        allowed_teams: list[str] | None = None,
+        allowed_teams: list[str] >> None = None,
         config_error: str | None = None,
     ):
         """Initialize repo configuration.
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_RShift, occurrence: 4
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -90,7 +90,7 @@
         required_title_patterns: list[str] | None = None,
         allowed_users: list[str] | None = None,
         allowed_teams: list[str] | None = None,
-        config_error: str | None = None,
+        config_error: str >> None = None,
     ):
         """Initialize repo configuration.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_RShift, occurrence: 5
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -136,7 +136,7 @@
         pr_labels: list[str],
         pr_title: str,
         pr_author: str,
-        author_team_slugs: list[str] | None = None,
+        author_team_slugs: list[str] >> None = None,
     ) -> tuple[bool, str | None]:
         """Check if a PR is eligible for auto-approval based on filters.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_RShift, occurrence: 6
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -137,7 +137,7 @@
         pr_title: str,
         pr_author: str,
         author_team_slugs: list[str] | None = None,
-    ) -> tuple[bool, str | None]:
+    ) -> tuple[bool, str >> None]:
         """Check if a PR is eligible for auto-approval based on filters.
 
         All configured filters must pass (AND logic between filter types).
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_LShift, occurrence: 0
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -86,7 +86,7 @@
         chatops_required_permission: str,
         approve_commands: list[str],
         unapprove_commands: list[str],
-        required_labels: list[str] | None = None,
+        required_labels: list[str] << None = None,
         required_title_patterns: list[str] | None = None,
         allowed_users: list[str] | None = None,
         allowed_teams: list[str] | None = None,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_LShift, occurrence: 1
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -87,7 +87,7 @@
         approve_commands: list[str],
         unapprove_commands: list[str],
         required_labels: list[str] | None = None,
-        required_title_patterns: list[str] | None = None,
+        required_title_patterns: list[str] << None = None,
         allowed_users: list[str] | None = None,
         allowed_teams: list[str] | None = None,
         config_error: str | None = None,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_LShift, occurrence: 2
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -88,7 +88,7 @@
         unapprove_commands: list[str],
         required_labels: list[str] | None = None,
         required_title_patterns: list[str] | None = None,
-        allowed_users: list[str] | None = None,
+        allowed_users: list[str] << None = None,
         allowed_teams: list[str] | None = None,
         config_error: str | None = None,
     ):
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_LShift, occurrence: 3
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -89,7 +89,7 @@
         required_labels: list[str] | None = None,
         required_title_patterns: list[str] | None = None,
         allowed_users: list[str] | None = None,
-        allowed_teams: list[str] | None = None,
+        allowed_teams: list[str] << None = None,
         config_error: str | None = None,
     ):
         """Initialize repo configuration.
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_LShift, occurrence: 4
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -90,7 +90,7 @@
         required_title_patterns: list[str] | None = None,
         allowed_users: list[str] | None = None,
         allowed_teams: list[str] | None = None,
-        config_error: str | None = None,
+        config_error: str << None = None,
     ):
         """Initialize repo configuration.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_LShift, occurrence: 5
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -136,7 +136,7 @@
         pr_labels: list[str],
         pr_title: str,
         pr_author: str,
-        author_team_slugs: list[str] | None = None,
+        author_team_slugs: list[str] << None = None,
     ) -> tuple[bool, str | None]:
         """Check if a PR is eligible for auto-approval based on filters.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/ReplaceBinaryOperator_BitOr_LShift, occurrence: 6
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -137,7 +137,7 @@
         pr_title: str,
         pr_author: str,
         author_team_slugs: list[str] | None = None,
-    ) -> tuple[bool, str | None]:
+    ) -> tuple[bool, str << None]:
         """Check if a PR is eligible for auto-approval based on filters.
 
         All configured filters must pass (AND logic between filter types).
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_BitAnd, occurrence: 0
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -86,7 +86,7 @@
         chatops_required_permission: str,
         approve_commands: list[str],
         unapprove_commands: list[str],
-        required_labels: list[str] | None = None,
+        required_labels: list[str] & None = None,
         required_title_patterns: list[str] | None = None,
         allowed_users: list[str] | None = None,
         allowed_teams: list[str] | None = None,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_BitAnd, occurrence: 1
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -87,7 +87,7 @@
         approve_commands: list[str],
         unapprove_commands: list[str],
         required_labels: list[str] | None = None,
-        required_title_patterns: list[str] | None = None,
+        required_title_patterns: list[str] & None = None,
         allowed_users: list[str] | None = None,
         allowed_teams: list[str] | None = None,
         config_error: str | None = None,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_BitAnd, occurrence: 2
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -88,7 +88,7 @@
         unapprove_commands: list[str],
         required_labels: list[str] | None = None,
         required_title_patterns: list[str] | None = None,
-        allowed_users: list[str] | None = None,
+        allowed_users: list[str] & None = None,
         allowed_teams: list[str] | None = None,
         config_error: str | None = None,
     ):
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceBinaryOperator_BitOr_BitAnd, occurrence: 3
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -89,7 +89,7 @@
         required_labels: list[str] | None = None,
         required_title_patterns: list[str] | None = None,
         allowed_users: list[str] | None = None,
-        allowed_teams: list[str] | None = None,
+        allowed_teams: list[str] & None = None,
         config_error: str | None = None,
     ):
         """Initialize repo configuration.
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/ReplaceBinaryOperator_BitOr_BitAnd, occurrence: 4
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -90,7 +90,7 @@
         required_title_patterns: list[str] | None = None,
         allowed_users: list[str] | None = None,
         allowed_teams: list[str] | None = None,
-        config_error: str | None = None,
+        config_error: str & None = None,
     ):
         """Initialize repo configuration.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_BitAnd, occurrence: 5
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -136,7 +136,7 @@
         pr_labels: list[str],
         pr_title: str,
         pr_author: str,
-        author_team_slugs: list[str] | None = None,
+        author_team_slugs: list[str] & None = None,
     ) -> tuple[bool, str | None]:
         """Check if a PR is eligible for auto-approval based on filters.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_BitAnd, occurrence: 6
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -137,7 +137,7 @@
         pr_title: str,
         pr_author: str,
         author_team_slugs: list[str] | None = None,
-    ) -> tuple[bool, str | None]:
+    ) -> tuple[bool, str & None]:
         """Check if a PR is eligible for auto-approval based on filters.
 
         All configured filters must pass (AND logic between filter types).
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_BitXor, occurrence: 0
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -86,7 +86,7 @@
         chatops_required_permission: str,
         approve_commands: list[str],
         unapprove_commands: list[str],
-        required_labels: list[str] | None = None,
+        required_labels: list[str] ^ None = None,
         required_title_patterns: list[str] | None = None,
         allowed_users: list[str] | None = None,
         allowed_teams: list[str] | None = None,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/ReplaceBinaryOperator_BitOr_BitXor, occurrence: 1
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -87,7 +87,7 @@
         approve_commands: list[str],
         unapprove_commands: list[str],
         required_labels: list[str] | None = None,
-        required_title_patterns: list[str] | None = None,
+        required_title_patterns: list[str] ^ None = None,
         allowed_users: list[str] | None = None,
         allowed_teams: list[str] | None = None,
         config_error: str | None = None,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_BitXor, occurrence: 2
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -88,7 +88,7 @@
         unapprove_commands: list[str],
         required_labels: list[str] | None = None,
         required_title_patterns: list[str] | None = None,
-        allowed_users: list[str] | None = None,
+        allowed_users: list[str] ^ None = None,
         allowed_teams: list[str] | None = None,
         config_error: str | None = None,
     ):
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_BitXor, occurrence: 3
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -89,7 +89,7 @@
         required_labels: list[str] | None = None,
         required_title_patterns: list[str] | None = None,
         allowed_users: list[str] | None = None,
-        allowed_teams: list[str] | None = None,
+        allowed_teams: list[str] ^ None = None,
         config_error: str | None = None,
     ):
         """Initialize repo configuration.
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_BitXor, occurrence: 4
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -90,7 +90,7 @@
         required_title_patterns: list[str] | None = None,
         allowed_users: list[str] | None = None,
         allowed_teams: list[str] | None = None,
-        config_error: str | None = None,
+        config_error: str ^ None = None,
     ):
         """Initialize repo configuration.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_BitXor, occurrence: 5
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -136,7 +136,7 @@
         pr_labels: list[str],
         pr_title: str,
         pr_author: str,
-        author_team_slugs: list[str] | None = None,
+        author_team_slugs: list[str] ^ None = None,
     ) -> tuple[bool, str | None]:
         """Check if a PR is eligible for auto-approval based on filters.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.64s
operator: core/ReplaceBinaryOperator_BitOr_BitXor, occurrence: 6
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -137,7 +137,7 @@
         pr_title: str,
         pr_author: str,
         author_team_slugs: list[str] | None = None,
-    ) -> tuple[bool, str | None]:
+    ) -> tuple[bool, str ^ None]:
         """Check if a PR is eligible for auto-approval based on filters.
 
         All configured filters must pass (AND logic between filter types).
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/ReplaceUnaryOperator_USub_UAdd, occurrence: 0
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -177,7 +177,7 @@
             if self.allowed_teams and author_team_slugs:
                 for team in self.allowed_teams:
                     # Support both "org/team" and "team" formats
-                    team_slug = team.split("/")[-1] if "/" in team else team
+                    team_slug = team.split("/")[+1] if "/" in team else team
                     if team_slug in author_team_slugs:
                         return True, None
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceUnaryOperator_USub_Invert, occurrence: 0
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -177,7 +177,7 @@
             if self.allowed_teams and author_team_slugs:
                 for team in self.allowed_teams:
                     # Support both "org/team" and "team" formats
-                    team_slug = team.split("/")[-1] if "/" in team else team
+                    team_slug = team.split("/")[~1] if "/" in team else team
                     if team_slug in author_team_slugs:
                         return True, None
 
..........................F
=================================== FAILURES ===================================
_______________________ test_is_pr_eligible_allowed_team _______________________

    def test_is_pr_eligible_allowed_team():
        """Test is_pr_eligible returns True when author is in an allowed team."""
        config = RepoConfig.from_toml('allowed_teams = ["my-org/release-team"]')
        is_eligible, reason = config.is_pr_eligible(
            [], "Update deps", "team-member", author_team_slugs=["release-team"]
        )
>       assert is_eligible is True
E       assert False is True

tests/test_config.py:311: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_is_pr_eligible_allowed_team - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 26 passed, 3 deselected in 0.45s
operator: core/ReplaceUnaryOperator_USub_Not, occurrence: 0
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -177,7 +177,7 @@
             if self.allowed_teams and author_team_slugs:
                 for team in self.allowed_teams:
                     # Support both "org/team" and "team" formats
-                    team_slug = team.split("/")[-1] if "/" in team else team
+                    team_slug = team.split("/")[not 1] if "/" in team else team
                     if team_slug in author_team_slugs:
                         return True, None
 
..........................F
=================================== FAILURES ===================================
_______________________ test_is_pr_eligible_allowed_team _______________________

    def test_is_pr_eligible_allowed_team():
        """Test is_pr_eligible returns True when author is in an allowed team."""
        config = RepoConfig.from_toml('allowed_teams = ["my-org/release-team"]')
        is_eligible, reason = config.is_pr_eligible(
            [], "Update deps", "team-member", author_team_slugs=["release-team"]
        )
>       assert is_eligible is True
E       assert False is True

tests/test_config.py:311: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_is_pr_eligible_allowed_team - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 26 passed, 3 deselected in 0.45s
operator: core/ReplaceUnaryOperator_Delete_USub, occurrence: 0
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -177,7 +177,7 @@
             if self.allowed_teams and author_team_slugs:
                 for team in self.allowed_teams:
                     # Support both "org/team" and "team" formats
-                    team_slug = team.split("/")[-1] if "/" in team else team
+                    team_slug = team.split("/")[1] if "/" in team else team
                     if team_slug in author_team_slugs:
                         return True, None
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceUnaryOperator_Delete_Not, occurrence: 0
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -156,7 +156,7 @@
 
         # Check required labels filter
         if self.required_labels:
-            if not any(label in self.required_labels for label in pr_labels):
+            if  any(label in self.required_labels for label in pr_labels):
                 return (
                     False,
                     f"PR missing required label (one of: {', '.join(self.required_labels)})",
...............F
=================================== FAILURES ===================================
__________________ test_is_pr_eligible_required_label_present __________________

    def test_is_pr_eligible_required_label_present():
        """Test is_pr_eligible returns True when required label is present."""
        config = RepoConfig.from_toml('required_labels = ["dependencies", "automated"]')
        is_eligible, reason = config.is_pr_eligible(["dependencies"], "PR title", "someuser")
>       assert is_eligible is True
E       assert False is True

tests/test_config.py:205: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_is_pr_eligible_required_label_present - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 15 passed, 3 deselected in 0.44s
operator: core/ReplaceUnaryOperator_Delete_Not, occurrence: 1
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -164,7 +164,7 @@
 
         # Check required title patterns filter
         if self.required_title_patterns:
-            if not any(re.search(pattern, pr_title) for pattern in self.required_title_patterns):
+            if  any(re.search(pattern, pr_title) for pattern in self.required_title_patterns):
                 return False, "PR title does not match any required pattern"
 
         # Check allowed users/teams filter (if either is configured)
..................F
=================================== FAILURES ===================================
__________________ test_is_pr_eligible_title_pattern_matches ___________________

    def test_is_pr_eligible_title_pattern_matches():
        """Test is_pr_eligible returns True when title matches pattern."""
        config = RepoConfig.from_toml('required_title_patterns = ["^chore:", "^\\\\[bot\\\\]"]')
        is_eligible, reason = config.is_pr_eligible([], "chore: update dependencies", "someuser")
>       assert is_eligible is True
E       assert False is True

tests/test_config.py:230: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_is_pr_eligible_title_pattern_matches - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 18 passed, 3 deselected in 0.44s
operator: core/ReplaceUnaryOperator_Delete_Not, occurrence: 2
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -200,7 +200,7 @@
         Returns:
             True if team check is needed (author not in allowed_users but teams configured)
         """
-        if not self.allowed_teams:
+        if  self.allowed_teams:
             return False
         if self.allowed_users and pr_author in self.allowed_users:
             return False
................................F
=================================== FAILURES ===================================
__________________ test_needs_team_check_no_teams_configured ___________________

    def test_needs_team_check_no_teams_configured():
        """Test needs_team_check returns False when no teams configured."""
        config = RepoConfig.from_toml('allowed_users = ["some-user"]')
>       assert config.needs_team_check("any-user") is False
E       AssertionError: assert True is False
E        +  where True = needs_team_check('any-user')
E        +    where needs_team_check = <stampbot.config.RepoConfig object at 0x7f2c71fd0aa0>.needs_team_check

tests/test_config.py:383: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_needs_team_check_no_teams_configured - AssertionError: assert True is False
 +  where True = needs_team_check('any-user')
 +    where needs_team_check = <stampbot.config.RepoConfig object at 0x7f2c71fd0aa0>.needs_team_check
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 32 passed, 3 deselected in 0.46s
operator: core/AddNot, occurrence: 0
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -155,7 +155,7 @@
         import re
 
         # Check required labels filter
-        if self.required_labels:
+        if not self.required_labels:
             if not any(label in self.required_labels for label in pr_labels):
                 return (
                     False,
..............F
=================================== FAILURES ===================================
________________________ test_is_pr_eligible_no_filters ________________________

    def test_is_pr_eligible_no_filters():
        """Test is_pr_eligible returns True when no filters configured."""
        config = RepoConfig.default()
        is_eligible, reason = config.is_pr_eligible(["some-label"], "Some PR title", "someuser")
>       assert is_eligible is True
E       assert False is True

tests/test_config.py:197: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_is_pr_eligible_no_filters - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 14 passed, 3 deselected in 0.44s
operator: core/AddNot, occurrence: 1
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -156,7 +156,7 @@
 
         # Check required labels filter
         if self.required_labels:
-            if not any(label in self.required_labels for label in pr_labels):
+            if not not any(label in self.required_labels for label in pr_labels):
                 return (
                     False,
                     f"PR missing required label (one of: {', '.join(self.required_labels)})",
...............F
=================================== FAILURES ===================================
__________________ test_is_pr_eligible_required_label_present __________________

    def test_is_pr_eligible_required_label_present():
        """Test is_pr_eligible returns True when required label is present."""
        config = RepoConfig.from_toml('required_labels = ["dependencies", "automated"]')
        is_eligible, reason = config.is_pr_eligible(["dependencies"], "PR title", "someuser")
>       assert is_eligible is True
E       assert False is True

tests/test_config.py:205: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_is_pr_eligible_required_label_present - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 15 passed, 3 deselected in 0.44s
operator: core/AddNot, occurrence: 2
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -163,7 +163,7 @@
                 )
 
         # Check required title patterns filter
-        if self.required_title_patterns:
+        if not self.required_title_patterns:
             if not any(re.search(pattern, pr_title) for pattern in self.required_title_patterns):
                 return False, "PR title does not match any required pattern"
 
..............F
=================================== FAILURES ===================================
________________________ test_is_pr_eligible_no_filters ________________________

    def test_is_pr_eligible_no_filters():
        """Test is_pr_eligible returns True when no filters configured."""
        config = RepoConfig.default()
        is_eligible, reason = config.is_pr_eligible(["some-label"], "Some PR title", "someuser")
>       assert is_eligible is True
E       assert False is True

tests/test_config.py:197: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_is_pr_eligible_no_filters - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 14 passed, 3 deselected in 0.44s
operator: core/AddNot, occurrence: 3
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -164,7 +164,7 @@
 
         # Check required title patterns filter
         if self.required_title_patterns:
-            if not any(re.search(pattern, pr_title) for pattern in self.required_title_patterns):
+            if not not any(re.search(pattern, pr_title) for pattern in self.required_title_patterns):
                 return False, "PR title does not match any required pattern"
 
         # Check allowed users/teams filter (if either is configured)
..................F
=================================== FAILURES ===================================
__________________ test_is_pr_eligible_title_pattern_matches ___________________

    def test_is_pr_eligible_title_pattern_matches():
        """Test is_pr_eligible returns True when title matches pattern."""
        config = RepoConfig.from_toml('required_title_patterns = ["^chore:", "^\\\\[bot\\\\]"]')
        is_eligible, reason = config.is_pr_eligible([], "chore: update dependencies", "someuser")
>       assert is_eligible is True
E       assert False is True

tests/test_config.py:230: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_is_pr_eligible_title_pattern_matches - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 18 passed, 3 deselected in 0.44s
operator: core/AddNot, occurrence: 4
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -168,7 +168,7 @@
                 return False, "PR title does not match any required pattern"
 
         # Check allowed users/teams filter (if either is configured)
-        if self.allowed_users or self.allowed_teams:
+        if not self.allowed_users or self.allowed_teams:
             # User is allowed if they're in allowed_users
             if pr_author in self.allowed_users:
                 return True, None
..............F
=================================== FAILURES ===================================
________________________ test_is_pr_eligible_no_filters ________________________

    def test_is_pr_eligible_no_filters():
        """Test is_pr_eligible returns True when no filters configured."""
        config = RepoConfig.default()
        is_eligible, reason = config.is_pr_eligible(["some-label"], "Some PR title", "someuser")
>       assert is_eligible is True
E       assert False is True

tests/test_config.py:197: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_is_pr_eligible_no_filters - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 14 passed, 3 deselected in 0.44s
operator: core/AddNot, occurrence: 5
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -170,7 +170,7 @@
         # Check allowed users/teams filter (if either is configured)
         if self.allowed_users or self.allowed_teams:
             # User is allowed if they're in allowed_users
-            if pr_author in self.allowed_users:
+            if not pr_author in self.allowed_users:
                 return True, None
 
             # Or if they're a member of an allowed team
........................F
=================================== FAILURES ===================================
_______________________ test_is_pr_eligible_allowed_user _______________________

    def test_is_pr_eligible_allowed_user():
        """Test is_pr_eligible returns True when author is in allowed_users."""
        config = RepoConfig.from_toml('allowed_users = ["dependabot[bot]", "renovate[bot]"]')
        is_eligible, reason = config.is_pr_eligible([], "Update deps", "dependabot[bot]")
>       assert is_eligible is True
E       assert False is True

tests/test_config.py:293: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_is_pr_eligible_allowed_user - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 24 passed, 3 deselected in 0.46s
operator: core/AddNot, occurrence: 6
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -174,7 +174,7 @@
                 return True, None
 
             # Or if they're a member of an allowed team
-            if self.allowed_teams and author_team_slugs:
+            if not self.allowed_teams and author_team_slugs:
                 for team in self.allowed_teams:
                     # Support both "org/team" and "team" formats
                     team_slug = team.split("/")[-1] if "/" in team else team
..........................F
=================================== FAILURES ===================================
_______________________ test_is_pr_eligible_allowed_team _______________________

    def test_is_pr_eligible_allowed_team():
        """Test is_pr_eligible returns True when author is in an allowed team."""
        config = RepoConfig.from_toml('allowed_teams = ["my-org/release-team"]')
        is_eligible, reason = config.is_pr_eligible(
            [], "Update deps", "team-member", author_team_slugs=["release-team"]
        )
>       assert is_eligible is True
E       assert False is True

tests/test_config.py:311: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_is_pr_eligible_allowed_team - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 26 passed, 3 deselected in 0.44s
operator: core/AddNot, occurrence: 7
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -177,7 +177,7 @@
             if self.allowed_teams and author_team_slugs:
                 for team in self.allowed_teams:
                     # Support both "org/team" and "team" formats
-                    team_slug = team.split("/")[-1] if "/" in team else team
+                    team_slug = team.split("/")[-1] if not "/" in team else team
                     if team_slug in author_team_slugs:
                         return True, None
 
..........................F
=================================== FAILURES ===================================
_______________________ test_is_pr_eligible_allowed_team _______________________

    def test_is_pr_eligible_allowed_team():
        """Test is_pr_eligible returns True when author is in an allowed team."""
        config = RepoConfig.from_toml('allowed_teams = ["my-org/release-team"]')
        is_eligible, reason = config.is_pr_eligible(
            [], "Update deps", "team-member", author_team_slugs=["release-team"]
        )
>       assert is_eligible is True
E       assert False is True

tests/test_config.py:311: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_is_pr_eligible_allowed_team - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 26 passed, 3 deselected in 0.46s
operator: core/AddNot, occurrence: 8
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -178,7 +178,7 @@
                 for team in self.allowed_teams:
                     # Support both "org/team" and "team" formats
                     team_slug = team.split("/")[-1] if "/" in team else team
-                    if team_slug in author_team_slugs:
+                    if not team_slug in author_team_slugs:
                         return True, None
 
             # Neither user nor team matched
..........................F
=================================== FAILURES ===================================
_______________________ test_is_pr_eligible_allowed_team _______________________

    def test_is_pr_eligible_allowed_team():
        """Test is_pr_eligible returns True when author is in an allowed team."""
        config = RepoConfig.from_toml('allowed_teams = ["my-org/release-team"]')
        is_eligible, reason = config.is_pr_eligible(
            [], "Update deps", "team-member", author_team_slugs=["release-team"]
        )
>       assert is_eligible is True
E       assert False is True

tests/test_config.py:311: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_is_pr_eligible_allowed_team - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 26 passed, 3 deselected in 0.45s
operator: core/AddNot, occurrence: 9
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -182,7 +182,7 @@
                         return True, None
 
             # Neither user nor team matched
-            if self.allowed_users and self.allowed_teams:
+            if not self.allowed_users and self.allowed_teams:
                 return False, "PR author not in allowed users or teams"
             elif self.allowed_users:
                 return False, f"PR author not in allowed users: {', '.join(self.allowed_users)}"
...........................F
=================================== FAILURES ===================================
_____________________ test_is_pr_eligible_team_not_matched _____________________

    def test_is_pr_eligible_team_not_matched():
        """Test is_pr_eligible returns False when author is not in any allowed team."""
        config = RepoConfig.from_toml('allowed_teams = ["my-org/release-team"]')
        is_eligible, reason = config.is_pr_eligible(
            [], "Update deps", "non-member", author_team_slugs=["other-team"]
        )
        assert is_eligible is False
>       assert "not a member of any allowed team" in reason
E       AssertionError: assert 'not a member of any allowed team' in 'PR author not in allowed users or teams'

tests/test_config.py:322: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_is_pr_eligible_team_not_matched - AssertionError: assert 'not a member of any allowed team' in 'PR author not in allowed users or teams'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 27 passed, 3 deselected in 0.45s
operator: core/AddNot, occurrence: 10
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -200,7 +200,7 @@
         Returns:
             True if team check is needed (author not in allowed_users but teams configured)
         """
-        if not self.allowed_teams:
+        if not not self.allowed_teams:
             return False
         if self.allowed_users and pr_author in self.allowed_users:
             return False
................................F
=================================== FAILURES ===================================
__________________ test_needs_team_check_no_teams_configured ___________________

    def test_needs_team_check_no_teams_configured():
        """Test needs_team_check returns False when no teams configured."""
        config = RepoConfig.from_toml('allowed_users = ["some-user"]')
>       assert config.needs_team_check("any-user") is False
E       AssertionError: assert True is False
E        +  where True = needs_team_check('any-user')
E        +    where needs_team_check = <stampbot.config.RepoConfig object at 0x7f28452f4aa0>.needs_team_check

tests/test_config.py:383: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_needs_team_check_no_teams_configured - AssertionError: assert True is False
 +  where True = needs_team_check('any-user')
 +    where needs_team_check = <stampbot.config.RepoConfig object at 0x7f28452f4aa0>.needs_team_check
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 32 passed, 3 deselected in 0.46s
operator: core/AddNot, occurrence: 11
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -202,7 +202,7 @@
         """
         if not self.allowed_teams:
             return False
-        if self.allowed_users and pr_author in self.allowed_users:
+        if not self.allowed_users and pr_author in self.allowed_users:
             return False
         return True
 
.................................F
=================================== FAILURES ===================================
_________________ test_needs_team_check_user_in_allowed_users __________________

    def test_needs_team_check_user_in_allowed_users():
        """Test needs_team_check returns False when user is in allowed_users."""
        toml_content = """
    allowed_users = ["special-user"]
    allowed_teams = ["my-org/release-team"]
    """
        config = RepoConfig.from_toml(toml_content)
>       assert config.needs_team_check("special-user") is False
E       AssertionError: assert True is False
E        +  where True = needs_team_check('special-user')
E        +    where needs_team_check = <stampbot.config.RepoConfig object at 0x7fbeaf9d4cb0>.needs_team_check

tests/test_config.py:393: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_needs_team_check_user_in_allowed_users - AssertionError: assert True is False
 +  where True = needs_team_check('special-user')
 +    where needs_team_check = <stampbot.config.RepoConfig object at 0x7fbeaf9d4cb0>.needs_team_check
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 33 passed, 3 deselected in 0.46s
operator: core/AddNot, occurrence: 12
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -218,7 +218,7 @@
         defaults = REPO_CONFIG_DEFAULTS.copy()
 
         # Allow overriding defaults via settings.toml [defaults] section
-        if settings.get("defaults"):
+        if not settings.get("defaults"):
             settings_defaults = settings.defaults
             for key in defaults:
                 if hasattr(settings_defaults, key):
........F
=================================== FAILURES ===================================
_____________ test_repo_config_get_defaults_with_settings_override _____________

    def test_repo_config_get_defaults_with_settings_override():
        """Test _get_defaults respects settings.toml overrides."""
        with patch("stampbot.config.settings") as mock_settings:
            # Create a mock defaults object with only specific attributes
            # Using spec=[] means hasattr will return False for most attributes
            mock_defaults = MagicMock(
                spec=["approval_labels", "chatops_enabled", "chatops_required_permission"]
            )
            mock_defaults.approval_labels = ["custom-label"]
            mock_defaults.chatops_enabled = False
            mock_defaults.chatops_required_permission = "write"
    
            mock_settings.get.return_value = mock_defaults
            mock_settings.defaults = mock_defaults
    
            defaults = RepoConfig._get_defaults()
    
            # Should have the overridden values
>           assert defaults["approval_labels"] == ["custom-label"]
E           AssertionError: assert ['autoapprove', 'stamp'] == ['custom-label']
E             
E             At index 0 diff: 'autoapprove' != 'custom-label'
E             Left contains one more item: 'stamp'
E             
E             Full diff:
E               [
E             -     'custom-label',
E             +     'autoapprove',
E             +     'stamp',
E               ]

tests/test_config.py:122: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_repo_config_get_defaults_with_settings_override - AssertionError: assert ['autoapprove', 'stamp'] == ['custom-label']
  
  At index 0 diff: 'autoapprove' != 'custom-label'
  Left contains one more item: 'stamp'
  
  Full diff:
    [
  -     'custom-label',
  +     'autoapprove',
  +     'stamp',
    ]
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 8 passed, 3 deselected in 0.43s
operator: core/AddNot, occurrence: 13
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -221,7 +221,7 @@
         if settings.get("defaults"):
             settings_defaults = settings.defaults
             for key in defaults:
-                if hasattr(settings_defaults, key):
+                if not hasattr(settings_defaults, key):
                     defaults[key] = getattr(settings_defaults, key)
 
         return defaults
F
=================================== FAILURES ===================================
__________________________ test_repo_config_from_toml __________________________

    def test_repo_config_from_toml():
        """Test parsing repository config from TOML."""
        toml_content = """
    approval_labels = ["test", "autoapprove"]
    auto_approve_on_label = true
    chatops_enabled = true
    chatops_required_permission = "write"
    approve_commands = ["approve", "stamp"]
    unapprove_commands = ["unapprove"]
    """
>       config = RepoConfig.from_toml(toml_content)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_config.py:20: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/config.py:246: in from_toml
    defaults = cls._get_defaults()
               ^^^^^^^^^^^^^^^^^^^
stampbot/config.py:225: in _get_defaults
    defaults[key] = getattr(settings_defaults, key)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/dynaconf/utils/boxing.py:22: in __getattr__
    result = super().__getattr__(n_item, *args, **kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

A = <Box: {'approval_labels': ['autoapprove', 'stamp'], 'auto_approve_on_label': True, 'chatops_enabled': True, 'chatops_r...red_permission': 'maintain', 'approve_commands': ['approve', 'stamp'], 'unapprove_commands': ['unapprove', 'unstamp']}>
item = 'required_labels'

    def __getattr__(A,item):
    	B=item
    	try:
    		try:C=A.__getitem__(B,_ignore_default=_E)
    		except KeyError:C=object.__getattribute__(A,B)
    	except AttributeError as E:
    		if B=='__getstate__':raise BoxKeyError(B)from _A
    		if B==_H:raise BoxError('_box_config key must exist')from _A
    		if A._box_config[_F]:
    			D=A._safe_attr(B)
    			if D in A._box_config[_C]:return A.__getitem__(A._box_config[_C][D])
    		if A._box_config[_J]:return A.__get_default(B)
>   		raise BoxKeyError(str(E))from _A
E     dynaconf.vendor.box.exceptions.BoxKeyError: "'DynaBox' object has no attribute 'required_labels'"

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/dynaconf/vendor/box/box.py:177: BoxKeyError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_repo_config_from_toml - dynaconf.vendor.box.exceptions.BoxKeyError: "'DynaBox' object has no attribute 'required_labels'"
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 3 deselected in 0.44s
operator: core/AddNot, occurrence: 14
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -247,7 +247,7 @@
 
         # Parse the repo-specific TOML
         repo_settings: dict[str, Any] = {}
-        if toml_content:
+        if not toml_content:
             repo_settings = toml.loads(toml_content)
 
         # Merge: repo settings override defaults
F
=================================== FAILURES ===================================
__________________________ test_repo_config_from_toml __________________________

    def test_repo_config_from_toml():
        """Test parsing repository config from TOML."""
        toml_content = """
    approval_labels = ["test", "autoapprove"]
    auto_approve_on_label = true
    chatops_enabled = true
    chatops_required_permission = "write"
    approve_commands = ["approve", "stamp"]
    unapprove_commands = ["unapprove"]
    """
        config = RepoConfig.from_toml(toml_content)
>       assert "test" in config.approval_labels
E       AssertionError: assert 'test' in <BoxList: ['autoapprove', 'stamp']>
E        +  where <BoxList: ['autoapprove', 'stamp']> = <stampbot.config.RepoConfig object at 0x7f9d1ab26510>.approval_labels

tests/test_config.py:21: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_repo_config_from_toml - AssertionError: assert 'test' in <BoxList: ['autoapprove', 'stamp']>
 +  where <BoxList: ['autoapprove', 'stamp']> = <stampbot.config.RepoConfig object at 0x7f9d1ab26510>.approval_labels
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 3 deselected in 0.43s
operator: core/AddNot, occurrence: 15
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -256,7 +256,7 @@
         import re
 
         required_permission = merged.get("chatops_required_permission", "maintain")
-        if required_permission not in REPO_PERMISSION_LEVELS:
+        if not required_permission not in REPO_PERMISSION_LEVELS:
             raise ValueError(
                 "Invalid chatops_required_permission: "
                 f"{required_permission}. "
F
=================================== FAILURES ===================================
__________________________ test_repo_config_from_toml __________________________

    def test_repo_config_from_toml():
        """Test parsing repository config from TOML."""
        toml_content = """
    approval_labels = ["test", "autoapprove"]
    auto_approve_on_label = true
    chatops_enabled = true
    chatops_required_permission = "write"
    approve_commands = ["approve", "stamp"]
    unapprove_commands = ["unapprove"]
    """
>       config = RepoConfig.from_toml(toml_content)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_config.py:20: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'stampbot.config.RepoConfig'>
toml_content = '\napproval_labels = ["test", "autoapprove"]\nauto_approve_on_label = true\nchatops_enabled = true\nchatops_required_permission = "write"\napprove_commands = ["approve", "stamp"]\nunapprove_commands = ["unapprove"]\n'

    @classmethod
    def from_toml(cls, toml_content: str) -> RepoConfig:
        """Parse TOML content and merge with app defaults.
    
        Uses dynaconf to properly merge repo-specific settings with
        the app-level defaults, allowing repos to override only
        the settings they need to change.
    
        Args:
            toml_content: Raw TOML content from repo's stampbot.toml
    
        Returns:
            RepoConfig with merged settings
        """
        import toml
    
        # Start with defaults
        defaults = cls._get_defaults()
    
        # Parse the repo-specific TOML
        repo_settings: dict[str, Any] = {}
        if toml_content:
            repo_settings = toml.loads(toml_content)
    
        # Merge: repo settings override defaults
        merged = {**defaults, **repo_settings}
    
        import re
    
        required_permission = merged.get("chatops_required_permission", "maintain")
        if not required_permission not in REPO_PERMISSION_LEVELS:
>           raise ValueError(
                "Invalid chatops_required_permission: "
                f"{required_permission}. "
                f"Valid values: {', '.join(REPO_PERMISSION_LEVELS)}"
            )
E           ValueError: Invalid chatops_required_permission: write. Valid values: none, read, triage, write, maintain, admin

stampbot/config.py:260: ValueError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_repo_config_from_toml - ValueError: Invalid chatops_required_permission: write. Valid values: none, read, triage, write, maintain, admin
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 3 deselected in 0.43s
operator: core/ReplaceTrueWithFalse, occurrence: 0
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -13,7 +13,7 @@
 # These can be overridden in settings.toml or per-repo stampbot.toml
 REPO_CONFIG_DEFAULTS = {
     "approval_labels": ["autoapprove", "stamp"],
-    "auto_approve_on_label": True,
+    "auto_approve_on_label": False,
     "chatops_enabled": True,
     "chatops_required_permission": "maintain",
     "approve_commands": ["approve", "stamp"],
........F
=================================== FAILURES ===================================
_____________ test_repo_config_get_defaults_with_settings_override _____________

    def test_repo_config_get_defaults_with_settings_override():
        """Test _get_defaults respects settings.toml overrides."""
        with patch("stampbot.config.settings") as mock_settings:
            # Create a mock defaults object with only specific attributes
            # Using spec=[] means hasattr will return False for most attributes
            mock_defaults = MagicMock(
                spec=["approval_labels", "chatops_enabled", "chatops_required_permission"]
            )
            mock_defaults.approval_labels = ["custom-label"]
            mock_defaults.chatops_enabled = False
            mock_defaults.chatops_required_permission = "write"
    
            mock_settings.get.return_value = mock_defaults
            mock_settings.defaults = mock_defaults
    
            defaults = RepoConfig._get_defaults()
    
            # Should have the overridden values
            assert defaults["approval_labels"] == ["custom-label"]
            assert defaults["chatops_enabled"] is False
            assert defaults["chatops_required_permission"] == "write"
            # Non-overridden values should use REPO_CONFIG_DEFAULTS
>           assert defaults["auto_approve_on_label"] is True
E           assert False is True

tests/test_config.py:126: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_repo_config_get_defaults_with_settings_override - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 8 passed, 3 deselected in 0.43s
operator: core/ReplaceTrueWithFalse, occurrence: 1
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -14,7 +14,7 @@
 REPO_CONFIG_DEFAULTS = {
     "approval_labels": ["autoapprove", "stamp"],
     "auto_approve_on_label": True,
-    "chatops_enabled": True,
+    "chatops_enabled": False,
     "chatops_required_permission": "maintain",
     "approve_commands": ["approve", "stamp"],
     "unapprove_commands": ["unapprove", "unstamp"],
..........F
=================================== FAILURES ===================================
________________ test_repo_config_get_defaults_partial_override ________________

    def test_repo_config_get_defaults_partial_override():
        """Test _get_defaults with partial settings override."""
        with patch("stampbot.config.settings") as mock_settings:
            # Create a mock defaults object with only some attributes
            mock_defaults = MagicMock(spec=[])  # Empty spec means no attributes
            # But we can still check hasattr behavior by setting one attribute
            mock_defaults.approval_labels = ["override-only-this"]
    
            mock_settings.get.return_value = mock_defaults
            mock_settings.defaults = mock_defaults
    
            defaults = RepoConfig._get_defaults()
    
            # The overridden value
            assert defaults["approval_labels"] == ["override-only-this"]
            # Non-overridden values should use REPO_CONFIG_DEFAULTS
            assert defaults["auto_approve_on_label"] is True
>           assert defaults["chatops_enabled"] is True
E           assert False is True

tests/test_config.py:160: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_repo_config_get_defaults_partial_override - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 10 passed, 3 deselected in 0.43s
operator: core/ReplaceTrueWithFalse, occurrence: 2
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -39,7 +39,7 @@
     envvar_prefix="STAMPBOT",
     settings_files=["settings.toml", ".secrets.toml"],
     environments=False,  # We don't use [development]/[production] sections
-    load_dotenv=True,
+    load_dotenv=False,
 )
 
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceTrueWithFalse, occurrence: 3
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -171,7 +171,7 @@
         if self.allowed_users or self.allowed_teams:
             # User is allowed if they're in allowed_users
             if pr_author in self.allowed_users:
-                return True, None
+                return False, None
 
             # Or if they're a member of an allowed team
             if self.allowed_teams and author_team_slugs:
........................F
=================================== FAILURES ===================================
_______________________ test_is_pr_eligible_allowed_user _______________________

    def test_is_pr_eligible_allowed_user():
        """Test is_pr_eligible returns True when author is in allowed_users."""
        config = RepoConfig.from_toml('allowed_users = ["dependabot[bot]", "renovate[bot]"]')
        is_eligible, reason = config.is_pr_eligible([], "Update deps", "dependabot[bot]")
>       assert is_eligible is True
E       assert False is True

tests/test_config.py:293: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_is_pr_eligible_allowed_user - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 24 passed, 3 deselected in 0.45s
operator: core/ReplaceTrueWithFalse, occurrence: 4
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -179,7 +179,7 @@
                     # Support both "org/team" and "team" formats
                     team_slug = team.split("/")[-1] if "/" in team else team
                     if team_slug in author_team_slugs:
-                        return True, None
+                        return False, None
 
             # Neither user nor team matched
             if self.allowed_users and self.allowed_teams:
..........................F
=================================== FAILURES ===================================
_______________________ test_is_pr_eligible_allowed_team _______________________

    def test_is_pr_eligible_allowed_team():
        """Test is_pr_eligible returns True when author is in an allowed team."""
        config = RepoConfig.from_toml('allowed_teams = ["my-org/release-team"]')
        is_eligible, reason = config.is_pr_eligible(
            [], "Update deps", "team-member", author_team_slugs=["release-team"]
        )
>       assert is_eligible is True
E       assert False is True

tests/test_config.py:311: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_is_pr_eligible_allowed_team - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 26 passed, 3 deselected in 0.45s
operator: core/ReplaceTrueWithFalse, occurrence: 5
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -189,7 +189,7 @@
             else:
                 return False, "PR author not a member of any allowed team"
 
-        return True, None
+        return False, None
 
     def needs_team_check(self, pr_author: str) -> bool:
         """Check if team membership verification is needed for this author.
..............F
=================================== FAILURES ===================================
________________________ test_is_pr_eligible_no_filters ________________________

    def test_is_pr_eligible_no_filters():
        """Test is_pr_eligible returns True when no filters configured."""
        config = RepoConfig.default()
        is_eligible, reason = config.is_pr_eligible(["some-label"], "Some PR title", "someuser")
>       assert is_eligible is True
E       assert False is True

tests/test_config.py:197: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_is_pr_eligible_no_filters - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 14 passed, 3 deselected in 0.44s
operator: core/ReplaceTrueWithFalse, occurrence: 6
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -204,7 +204,7 @@
             return False
         if self.allowed_users and pr_author in self.allowed_users:
             return False
-        return True
+        return False
 
     @classmethod
     def _get_defaults(cls) -> dict[str, Any]:
..................................F
=================================== FAILURES ===================================
_______________ test_needs_team_check_user_not_in_allowed_users ________________

    def test_needs_team_check_user_not_in_allowed_users():
        """Test needs_team_check returns True when user not in allowed_users but teams configured."""
        toml_content = """
    allowed_users = ["special-user"]
    allowed_teams = ["my-org/release-team"]
    """
        config = RepoConfig.from_toml(toml_content)
>       assert config.needs_team_check("other-user") is True
E       AssertionError: assert False is True
E        +  where False = needs_team_check('other-user')
E        +    where needs_team_check = <stampbot.config.RepoConfig object at 0x7eff26e60ec0>.needs_team_check

tests/test_config.py:403: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_needs_team_check_user_not_in_allowed_users - AssertionError: assert False is True
 +  where False = needs_team_check('other-user')
 +    where needs_team_check = <stampbot.config.RepoConfig object at 0x7eff26e60ec0>.needs_team_check
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 34 passed, 3 deselected in 0.46s
operator: core/ReplaceTrueWithFalse, occurrence: 7
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -273,7 +273,7 @@
 
         return cls(
             approval_labels=merged.get("approval_labels", []),
-            auto_approve_on_label=merged.get("auto_approve_on_label", True),
+            auto_approve_on_label=merged.get("auto_approve_on_label", False),
             chatops_enabled=merged.get("chatops_enabled", True),
             chatops_required_permission=required_permission,
             approve_commands=merged.get("approve_commands", ["approve", "stamp"]),
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceTrueWithFalse, occurrence: 8
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -274,7 +274,7 @@
         return cls(
             approval_labels=merged.get("approval_labels", []),
             auto_approve_on_label=merged.get("auto_approve_on_label", True),
-            chatops_enabled=merged.get("chatops_enabled", True),
+            chatops_enabled=merged.get("chatops_enabled", False),
             chatops_required_permission=required_permission,
             approve_commands=merged.get("approve_commands", ["approve", "stamp"]),
             unapprove_commands=merged.get("unapprove_commands", ["unapprove", "unstamp"]),
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceFalseWithTrue, occurrence: 0
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -38,7 +38,7 @@
 settings = Dynaconf(
     envvar_prefix="STAMPBOT",
     settings_files=["settings.toml", ".secrets.toml"],
-    environments=False,  # We don't use [development]/[production] sections
+    environments=True,  # We don't use [development]/[production] sections
     load_dotenv=True,
 )
 
........................................................................ [ 34%]
...................F
=================================== FAILURES ===================================
__________________ test_lifespan_startup_shutdown_configured ___________________

    def test_lifespan_startup_shutdown_configured():
        """Test app lifespan startup and shutdown when configured."""
>       from stampbot.main import app

tests/test_main.py:15: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/main.py:41: in <module>
    configure_logging()
stampbot/logger.py:58: in configure_logging
    log_level_int = logging.getLevelName(settings.log_level)
                                         ^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/dynaconf/base.py:144: in __getattr__
    value = getattr(self._wrapped, name)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <dynaconf.base.Settings object at 0x7f1149d49400>, name = 'LOG_LEVEL'

    def __getattribute__(self, name):
        if (
            name.startswith("__")
            or name in RESERVED_ATTRS + UPPER_DEFAULT_SETTINGS
        ):
            return super().__getattribute__(name)
    
        # This is to keep the only upper case mode working
        # self._store has Lazy values already evaluated
        if (
            name.islower()
            and self._store.get("LOWERCASE_READ_FOR_DYNACONF", empty) is False
        ):
            try:
                # only matches exact casing, first levels always upper
                return self._store.__getattribute__(name)
            except KeyError:
                return super().__getattribute__(name)
    
        # then go to the regular .get which triggers hooks among other things
        value = self.get(name, default=empty)
        if value is empty:
>           return super().__getattribute__(name)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E           AttributeError: 'Settings' object has no attribute 'LOG_LEVEL'

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/dynaconf/base.py:325: AttributeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_lifespan_startup_shutdown_configured - AttributeError: 'Settings' object has no attribute 'LOG_LEVEL'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 91 passed, 3 deselected in 0.83s
operator: core/ReplaceFalseWithTrue, occurrence: 1
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -158,7 +158,7 @@
         if self.required_labels:
             if not any(label in self.required_labels for label in pr_labels):
                 return (
-                    False,
+                    True,
                     f"PR missing required label (one of: {', '.join(self.required_labels)})",
                 )
 
................F
=================================== FAILURES ===================================
__________________ test_is_pr_eligible_required_label_missing __________________

    def test_is_pr_eligible_required_label_missing():
        """Test is_pr_eligible returns False when required label is missing."""
        config = RepoConfig.from_toml('required_labels = ["dependencies", "automated"]')
        is_eligible, reason = config.is_pr_eligible(["other-label"], "PR title", "someuser")
>       assert is_eligible is False
E       assert True is False

tests/test_config.py:213: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_is_pr_eligible_required_label_missing - assert True is False
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 16 passed, 3 deselected in 0.45s
operator: core/ReplaceFalseWithTrue, occurrence: 2
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -165,7 +165,7 @@
         # Check required title patterns filter
         if self.required_title_patterns:
             if not any(re.search(pattern, pr_title) for pattern in self.required_title_patterns):
-                return False, "PR title does not match any required pattern"
+                return True, "PR title does not match any required pattern"
 
         # Check allowed users/teams filter (if either is configured)
         if self.allowed_users or self.allowed_teams:
...................F
=================================== FAILURES ===================================
__________________ test_is_pr_eligible_title_pattern_no_match __________________

    def test_is_pr_eligible_title_pattern_no_match():
        """Test is_pr_eligible returns False when title doesn't match any pattern."""
        config = RepoConfig.from_toml('required_title_patterns = ["^chore:", "^\\\\[bot\\\\]"]')
        is_eligible, reason = config.is_pr_eligible([], "feat: add new feature", "someuser")
>       assert is_eligible is False
E       assert True is False

tests/test_config.py:238: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_is_pr_eligible_title_pattern_no_match - assert True is False
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 19 passed, 3 deselected in 0.45s
operator: core/ReplaceFalseWithTrue, occurrence: 3
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -183,7 +183,7 @@
 
             # Neither user nor team matched
             if self.allowed_users and self.allowed_teams:
-                return False, "PR author not in allowed users or teams"
+                return True, "PR author not in allowed users or teams"
             elif self.allowed_users:
                 return False, f"PR author not in allowed users: {', '.join(self.allowed_users)}"
             else:
.............................F
=================================== FAILURES ===================================
__________________ test_is_pr_eligible_neither_user_nor_team ___________________

    def test_is_pr_eligible_neither_user_nor_team():
        """Test is_pr_eligible returns False when neither user nor team matches."""
        toml_content = """
    allowed_users = ["special-user"]
    allowed_teams = ["my-org/release-team"]
    """
        config = RepoConfig.from_toml(toml_content)
        is_eligible, reason = config.is_pr_eligible(
            [], "Update deps", "random-user", author_team_slugs=["other-team"]
        )
>       assert is_eligible is False
E       assert True is False

tests/test_config.py:348: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_is_pr_eligible_neither_user_nor_team - assert True is False
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 29 passed, 3 deselected in 0.45s
operator: core/ReplaceFalseWithTrue, occurrence: 4
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -185,7 +185,7 @@
             if self.allowed_users and self.allowed_teams:
                 return False, "PR author not in allowed users or teams"
             elif self.allowed_users:
-                return False, f"PR author not in allowed users: {', '.join(self.allowed_users)}"
+                return True, f"PR author not in allowed users: {', '.join(self.allowed_users)}"
             else:
                 return False, "PR author not a member of any allowed team"
 
.........................F
=================================== FAILURES ===================================
_____________________ test_is_pr_eligible_user_not_allowed _____________________

    def test_is_pr_eligible_user_not_allowed():
        """Test is_pr_eligible returns False when author is not in allowed_users."""
        config = RepoConfig.from_toml('allowed_users = ["dependabot[bot]", "renovate[bot]"]')
        is_eligible, reason = config.is_pr_eligible([], "Update deps", "random-user")
>       assert is_eligible is False
E       assert True is False

tests/test_config.py:301: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_is_pr_eligible_user_not_allowed - assert True is False
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 25 passed, 3 deselected in 0.45s
operator: core/ReplaceFalseWithTrue, occurrence: 5
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -187,7 +187,7 @@
             elif self.allowed_users:
                 return False, f"PR author not in allowed users: {', '.join(self.allowed_users)}"
             else:
-                return False, "PR author not a member of any allowed team"
+                return True, "PR author not a member of any allowed team"
 
         return True, None
 
...........................F
=================================== FAILURES ===================================
_____________________ test_is_pr_eligible_team_not_matched _____________________

    def test_is_pr_eligible_team_not_matched():
        """Test is_pr_eligible returns False when author is not in any allowed team."""
        config = RepoConfig.from_toml('allowed_teams = ["my-org/release-team"]')
        is_eligible, reason = config.is_pr_eligible(
            [], "Update deps", "non-member", author_team_slugs=["other-team"]
        )
>       assert is_eligible is False
E       assert True is False

tests/test_config.py:321: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_is_pr_eligible_team_not_matched - assert True is False
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 27 passed, 3 deselected in 0.45s
operator: core/ReplaceFalseWithTrue, occurrence: 6
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -201,7 +201,7 @@
             True if team check is needed (author not in allowed_users but teams configured)
         """
         if not self.allowed_teams:
-            return False
+            return True
         if self.allowed_users and pr_author in self.allowed_users:
             return False
         return True
................................F
=================================== FAILURES ===================================
__________________ test_needs_team_check_no_teams_configured ___________________

    def test_needs_team_check_no_teams_configured():
        """Test needs_team_check returns False when no teams configured."""
        config = RepoConfig.from_toml('allowed_users = ["some-user"]')
>       assert config.needs_team_check("any-user") is False
E       AssertionError: assert True is False
E        +  where True = needs_team_check('any-user')
E        +    where needs_team_check = <stampbot.config.RepoConfig object at 0x7f803b874aa0>.needs_team_check

tests/test_config.py:383: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_needs_team_check_no_teams_configured - AssertionError: assert True is False
 +  where True = needs_team_check('any-user')
 +    where needs_team_check = <stampbot.config.RepoConfig object at 0x7f803b874aa0>.needs_team_check
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 32 passed, 3 deselected in 0.46s
operator: core/ReplaceFalseWithTrue, occurrence: 7
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -203,7 +203,7 @@
         if not self.allowed_teams:
             return False
         if self.allowed_users and pr_author in self.allowed_users:
-            return False
+            return True
         return True
 
     @classmethod
.................................F
=================================== FAILURES ===================================
_________________ test_needs_team_check_user_in_allowed_users __________________

    def test_needs_team_check_user_in_allowed_users():
        """Test needs_team_check returns False when user is in allowed_users."""
        toml_content = """
    allowed_users = ["special-user"]
    allowed_teams = ["my-org/release-team"]
    """
        config = RepoConfig.from_toml(toml_content)
>       assert config.needs_team_check("special-user") is False
E       AssertionError: assert True is False
E        +  where True = needs_team_check('special-user')
E        +    where needs_team_check = <stampbot.config.RepoConfig object at 0x7f843825ccb0>.needs_team_check

tests/test_config.py:393: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_needs_team_check_user_in_allowed_users - AssertionError: assert True is False
 +  where True = needs_team_check('special-user')
 +    where needs_team_check = <stampbot.config.RepoConfig object at 0x7f843825ccb0>.needs_team_check
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 33 passed, 3 deselected in 0.46s
operator: core/ReplaceAndWithOr, occurrence: 0
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -174,7 +174,7 @@
                 return True, None
 
             # Or if they're a member of an allowed team
-            if self.allowed_teams and author_team_slugs:
+            if self.allowed_teams or author_team_slugs:
                 for team in self.allowed_teams:
                     # Support both "org/team" and "team" formats
                     team_slug = team.split("/")[-1] if "/" in team else team
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/ReplaceAndWithOr, occurrence: 1
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -182,7 +182,7 @@
                         return True, None
 
             # Neither user nor team matched
-            if self.allowed_users and self.allowed_teams:
+            if self.allowed_users or self.allowed_teams:
                 return False, "PR author not in allowed users or teams"
             elif self.allowed_users:
                 return False, f"PR author not in allowed users: {', '.join(self.allowed_users)}"
...........................F
=================================== FAILURES ===================================
_____________________ test_is_pr_eligible_team_not_matched _____________________

    def test_is_pr_eligible_team_not_matched():
        """Test is_pr_eligible returns False when author is not in any allowed team."""
        config = RepoConfig.from_toml('allowed_teams = ["my-org/release-team"]')
        is_eligible, reason = config.is_pr_eligible(
            [], "Update deps", "non-member", author_team_slugs=["other-team"]
        )
        assert is_eligible is False
>       assert "not a member of any allowed team" in reason
E       AssertionError: assert 'not a member of any allowed team' in 'PR author not in allowed users or teams'

tests/test_config.py:322: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_is_pr_eligible_team_not_matched - AssertionError: assert 'not a member of any allowed team' in 'PR author not in allowed users or teams'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 27 passed, 3 deselected in 0.48s
operator: core/ReplaceAndWithOr, occurrence: 2
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -202,7 +202,7 @@
         """
         if not self.allowed_teams:
             return False
-        if self.allowed_users and pr_author in self.allowed_users:
+        if self.allowed_users or pr_author in self.allowed_users:
             return False
         return True
 
..................................F
=================================== FAILURES ===================================
_______________ test_needs_team_check_user_not_in_allowed_users ________________

    def test_needs_team_check_user_not_in_allowed_users():
        """Test needs_team_check returns True when user not in allowed_users but teams configured."""
        toml_content = """
    allowed_users = ["special-user"]
    allowed_teams = ["my-org/release-team"]
    """
        config = RepoConfig.from_toml(toml_content)
>       assert config.needs_team_check("other-user") is True
E       AssertionError: assert False is True
E        +  where False = needs_team_check('other-user')
E        +    where needs_team_check = <stampbot.config.RepoConfig object at 0x7f8e9a58cec0>.needs_team_check

tests/test_config.py:403: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_needs_team_check_user_not_in_allowed_users - AssertionError: assert False is True
 +  where False = needs_team_check('other-user')
 +    where needs_team_check = <stampbot.config.RepoConfig object at 0x7f8e9a58cec0>.needs_team_check
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 34 passed, 3 deselected in 0.46s
operator: core/ReplaceOrWithAnd, occurrence: 0
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -113,7 +113,7 @@
         self.chatops_required_permission = chatops_required_permission
         self.approve_commands = approve_commands
         self.unapprove_commands = unapprove_commands
-        self.required_labels = required_labels or []
+        self.required_labels = required_labels and []
         self.required_title_patterns = required_title_patterns or []
         self.allowed_users = allowed_users or []
         self.allowed_teams = allowed_teams or []
...........F
=================================== FAILURES ===================================
____________________ test_repo_config_with_required_labels _____________________

    def test_repo_config_with_required_labels():
        """Test parsing repository config with required labels."""
        toml_content = """
    required_labels = ["dependencies", "automated"]
    """
        config = RepoConfig.from_toml(toml_content)
>       assert config.required_labels == ["dependencies", "automated"]
E       AssertionError: assert [] == ['dependencies', 'automated']
E         
E         Right contains 2 more items, first extra item: 'dependencies'
E         
E         Full diff:
E         + []
E         - [
E         -     'dependencies',
E         -     'automated',
E         - ]

tests/test_config.py:170: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_repo_config_with_required_labels - AssertionError: assert [] == ['dependencies', 'automated']
  
  Right contains 2 more items, first extra item: 'dependencies'
  
  Full diff:
  + []
  - [
  -     'dependencies',
  -     'automated',
  - ]
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 11 passed, 3 deselected in 0.45s
operator: core/ReplaceOrWithAnd, occurrence: 1
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -114,7 +114,7 @@
         self.approve_commands = approve_commands
         self.unapprove_commands = unapprove_commands
         self.required_labels = required_labels or []
-        self.required_title_patterns = required_title_patterns or []
+        self.required_title_patterns = required_title_patterns and []
         self.allowed_users = allowed_users or []
         self.allowed_teams = allowed_teams or []
         self.config_error = config_error
............F
=================================== FAILURES ===================================
________________ test_repo_config_with_required_title_patterns _________________

    def test_repo_config_with_required_title_patterns():
        """Test parsing repository config with required title patterns."""
        toml_content = """
    required_title_patterns = ["^\\\\[bot\\\\]", "^chore:"]
    """
        config = RepoConfig.from_toml(toml_content)
>       assert len(config.required_title_patterns) == 2
E       assert 0 == 2
E        +  where 0 = len([])
E        +    where [] = <stampbot.config.RepoConfig object at 0x7fec01009350>.required_title_patterns

tests/test_config.py:179: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_repo_config_with_required_title_patterns - assert 0 == 2
 +  where 0 = len([])
 +    where [] = <stampbot.config.RepoConfig object at 0x7fec01009350>.required_title_patterns
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 12 passed, 3 deselected in 0.44s
operator: core/ReplaceOrWithAnd, occurrence: 2
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -115,7 +115,7 @@
         self.unapprove_commands = unapprove_commands
         self.required_labels = required_labels or []
         self.required_title_patterns = required_title_patterns or []
-        self.allowed_users = allowed_users or []
+        self.allowed_users = allowed_users and []
         self.allowed_teams = allowed_teams or []
         self.config_error = config_error
 
.........................F
=================================== FAILURES ===================================
_____________________ test_is_pr_eligible_user_not_allowed _____________________

    def test_is_pr_eligible_user_not_allowed():
        """Test is_pr_eligible returns False when author is not in allowed_users."""
        config = RepoConfig.from_toml('allowed_users = ["dependabot[bot]", "renovate[bot]"]')
        is_eligible, reason = config.is_pr_eligible([], "Update deps", "random-user")
>       assert is_eligible is False
E       assert True is False

tests/test_config.py:301: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_is_pr_eligible_user_not_allowed - assert True is False
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 25 passed, 3 deselected in 0.45s
operator: core/ReplaceOrWithAnd, occurrence: 3
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -116,7 +116,7 @@
         self.required_labels = required_labels or []
         self.required_title_patterns = required_title_patterns or []
         self.allowed_users = allowed_users or []
-        self.allowed_teams = allowed_teams or []
+        self.allowed_teams = allowed_teams and []
         self.config_error = config_error
 
     def with_config_error(self, message: str) -> RepoConfig:
...........................F
=================================== FAILURES ===================================
_____________________ test_is_pr_eligible_team_not_matched _____________________

    def test_is_pr_eligible_team_not_matched():
        """Test is_pr_eligible returns False when author is not in any allowed team."""
        config = RepoConfig.from_toml('allowed_teams = ["my-org/release-team"]')
        is_eligible, reason = config.is_pr_eligible(
            [], "Update deps", "non-member", author_team_slugs=["other-team"]
        )
>       assert is_eligible is False
E       assert True is False

tests/test_config.py:321: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_is_pr_eligible_team_not_matched - assert True is False
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 27 passed, 3 deselected in 0.45s
operator: core/ReplaceOrWithAnd, occurrence: 4
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -168,7 +168,7 @@
                 return False, "PR title does not match any required pattern"
 
         # Check allowed users/teams filter (if either is configured)
-        if self.allowed_users or self.allowed_teams:
+        if self.allowed_users and self.allowed_teams:
             # User is allowed if they're in allowed_users
             if pr_author in self.allowed_users:
                 return True, None
.........................F
=================================== FAILURES ===================================
_____________________ test_is_pr_eligible_user_not_allowed _____________________

    def test_is_pr_eligible_user_not_allowed():
        """Test is_pr_eligible returns False when author is not in allowed_users."""
        config = RepoConfig.from_toml('allowed_users = ["dependabot[bot]", "renovate[bot]"]')
        is_eligible, reason = config.is_pr_eligible([], "Update deps", "random-user")
>       assert is_eligible is False
E       assert True is False

tests/test_config.py:301: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_is_pr_eligible_user_not_allowed - assert True is False
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 25 passed, 3 deselected in 0.45s
operator: core/ExceptionReplacer, occurrence: 0
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -268,7 +268,7 @@
         for pattern in title_patterns:
             try:
                 re.compile(pattern)
-            except re.error as e:
+            except reCosmicRayTestingExceptionerror as e:
                 raise ValueError(f"Invalid regex pattern '{pattern}': {e}") from e
 
         return cls(
.............F
=================================== FAILURES ===================================
____________________ test_repo_config_invalid_regex_pattern ____________________

cls = <class 'stampbot.config.RepoConfig'>
toml_content = '\nrequired_title_patterns = ["[invalid"]\n'

    @classmethod
    def from_toml(cls, toml_content: str) -> RepoConfig:
        """Parse TOML content and merge with app defaults.
    
        Uses dynaconf to properly merge repo-specific settings with
        the app-level defaults, allowing repos to override only
        the settings they need to change.
    
        Args:
            toml_content: Raw TOML content from repo's stampbot.toml
    
        Returns:
            RepoConfig with merged settings
        """
        import toml
    
        # Start with defaults
        defaults = cls._get_defaults()
    
        # Parse the repo-specific TOML
        repo_settings: dict[str, Any] = {}
        if toml_content:
            repo_settings = toml.loads(toml_content)
    
        # Merge: repo settings override defaults
        merged = {**defaults, **repo_settings}
    
        import re
    
        required_permission = merged.get("chatops_required_permission", "maintain")
        if required_permission not in REPO_PERMISSION_LEVELS:
            raise ValueError(
                "Invalid chatops_required_permission: "
                f"{required_permission}. "
                f"Valid values: {', '.join(REPO_PERMISSION_LEVELS)}"
            )
    
        # Validate regex patterns
        title_patterns = merged.get("required_title_patterns", [])
        for pattern in title_patterns:
            try:
>               re.compile(pattern)

stampbot/config.py:270: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/re/__init__.py:289: in compile
    return _compile(pattern, flags)
           ^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/re/__init__.py:350: in _compile
    p = _compiler.compile(pattern, flags)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/re/_compiler.py:762: in compile
    p = _parser.parse(p, flags)
        ^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/re/_parser.py:973: in parse
    p = _parse_sub(source, state, flags & SRE_FLAG_VERBOSE, 0)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/re/_parser.py:460: in _parse_sub
    itemsappend(_parse(source, state, verbose, nested + 1,
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

source = <re._parser.Tokenizer object at 0x7f7e74d79470>
state = <re._parser.State object at 0x7f7e74f5e5d0>, verbose = 0, nested = 1
first = True

    def _parse(source, state, verbose, nested, first=False):
        # parse a simple pattern
        subpattern = SubPattern(state)
    
        # precompute constants into local variables
        subpatternappend = subpattern.append
        sourceget = source.get
        sourcematch = source.match
        _len = len
        _ord = ord
    
        while True:
    
            this = source.next
            if this is None:
                break # end of pattern
            if this in "|)":
                break # end of subpattern
            sourceget()
    
            if verbose:
                # skip whitespace and comments
                if this in WHITESPACE:
                    continue
                if this == "#":
                    while True:
                        this = sourceget()
                        if this is None or this == "\n":
                            break
                    continue
    
            if this[0] == "\\":
                code = _escape(source, this, state)
                subpatternappend(code)
    
            elif this not in SPECIAL_CHARS:
                subpatternappend((LITERAL, _ord(this)))
    
            elif this == "[":
                here = source.tell() - 1
                # character set
                set = []
                setappend = set.append
    ##          if sourcematch(":"):
    ##              pass # handle character classes
                if source.next == '[':
                    import warnings
                    warnings.warn(
                        'Possible nested set at position %d' % source.tell(),
                        FutureWarning, stacklevel=nested + 6
                    )
                negate = sourcematch("^")
                # check remaining characters
                while True:
                    this = sourceget()
                    if this is None:
>                       raise source.error("unterminated character set",
                                           source.tell() - here)
E                       re.PatternError: unterminated character set at position 0

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/re/_parser.py:568: PatternError

During handling of the above exception, another exception occurred:

    def test_repo_config_invalid_regex_pattern():
        """Test invalid regex pattern raises a ValueError."""
        toml_content = """
    required_title_patterns = ["[invalid"]
    """
        with pytest.raises(ValueError, match="Invalid regex pattern"):
>           RepoConfig.from_toml(toml_content)

tests/test_config.py:190: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'stampbot.config.RepoConfig'>
toml_content = '\nrequired_title_patterns = ["[invalid"]\n'

    @classmethod
    def from_toml(cls, toml_content: str) -> RepoConfig:
        """Parse TOML content and merge with app defaults.
    
        Uses dynaconf to properly merge repo-specific settings with
        the app-level defaults, allowing repos to override only
        the settings they need to change.
    
        Args:
            toml_content: Raw TOML content from repo's stampbot.toml
    
        Returns:
            RepoConfig with merged settings
        """
        import toml
    
        # Start with defaults
        defaults = cls._get_defaults()
    
        # Parse the repo-specific TOML
        repo_settings: dict[str, Any] = {}
        if toml_content:
            repo_settings = toml.loads(toml_content)
    
        # Merge: repo settings override defaults
        merged = {**defaults, **repo_settings}
    
        import re
    
        required_permission = merged.get("chatops_required_permission", "maintain")
        if required_permission not in REPO_PERMISSION_LEVELS:
            raise ValueError(
                "Invalid chatops_required_permission: "
                f"{required_permission}. "
                f"Valid values: {', '.join(REPO_PERMISSION_LEVELS)}"
            )
    
        # Validate regex patterns
        title_patterns = merged.get("required_title_patterns", [])
        for pattern in title_patterns:
            try:
                re.compile(pattern)
>           except reCosmicRayTestingExceptionerror as e:
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E           NameError: name 'reCosmicRayTestingExceptionerror' is not defined

stampbot/config.py:271: NameError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_repo_config_invalid_regex_pattern - NameError: name 'reCosmicRayTestingExceptionerror' is not defined
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 13 passed, 3 deselected in 0.57s
operator: core/NumberReplacer, occurrence: 0
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -177,7 +177,7 @@
             if self.allowed_teams and author_team_slugs:
                 for team in self.allowed_teams:
                     # Support both "org/team" and "team" formats
-                    team_slug = team.split("/")[-1] if "/" in team else team
+                    team_slug = team.split("/")[- 2] if "/" in team else team
                     if team_slug in author_team_slugs:
                         return True, None
 
..........................F
=================================== FAILURES ===================================
_______________________ test_is_pr_eligible_allowed_team _______________________

    def test_is_pr_eligible_allowed_team():
        """Test is_pr_eligible returns True when author is in an allowed team."""
        config = RepoConfig.from_toml('allowed_teams = ["my-org/release-team"]')
        is_eligible, reason = config.is_pr_eligible(
            [], "Update deps", "team-member", author_team_slugs=["release-team"]
        )
>       assert is_eligible is True
E       assert False is True

tests/test_config.py:311: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_is_pr_eligible_allowed_team - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 26 passed, 3 deselected in 0.46s
operator: core/NumberReplacer, occurrence: 1
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -177,7 +177,7 @@
             if self.allowed_teams and author_team_slugs:
                 for team in self.allowed_teams:
                     # Support both "org/team" and "team" formats
-                    team_slug = team.split("/")[-1] if "/" in team else team
+                    team_slug = team.split("/")[- 0] if "/" in team else team
                     if team_slug in author_team_slugs:
                         return True, None
 
..........................F
=================================== FAILURES ===================================
_______________________ test_is_pr_eligible_allowed_team _______________________

    def test_is_pr_eligible_allowed_team():
        """Test is_pr_eligible returns True when author is in an allowed team."""
        config = RepoConfig.from_toml('allowed_teams = ["my-org/release-team"]')
        is_eligible, reason = config.is_pr_eligible(
            [], "Update deps", "team-member", author_team_slugs=["release-team"]
        )
>       assert is_eligible is True
E       assert False is True

tests/test_config.py:311: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_is_pr_eligible_allowed_team - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 26 passed, 3 deselected in 0.45s
operator: core/RemoveDecorator, occurrence: 0
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -205,8 +205,6 @@
         if self.allowed_users and pr_author in self.allowed_users:
             return False
         return True
-
-    @classmethod
     def _get_defaults(cls) -> dict[str, Any]:
         """Get default repo config values.
 
F
=================================== FAILURES ===================================
__________________________ test_repo_config_from_toml __________________________

    def test_repo_config_from_toml():
        """Test parsing repository config from TOML."""
        toml_content = """
    approval_labels = ["test", "autoapprove"]
    auto_approve_on_label = true
    chatops_enabled = true
    chatops_required_permission = "write"
    approve_commands = ["approve", "stamp"]
    unapprove_commands = ["unapprove"]
    """
>       config = RepoConfig.from_toml(toml_content)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_config.py:20: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'stampbot.config.RepoConfig'>
toml_content = '\napproval_labels = ["test", "autoapprove"]\nauto_approve_on_label = true\nchatops_enabled = true\nchatops_required_permission = "write"\napprove_commands = ["approve", "stamp"]\nunapprove_commands = ["unapprove"]\n'

    @classmethod
    def from_toml(cls, toml_content: str) -> RepoConfig:
        """Parse TOML content and merge with app defaults.
    
        Uses dynaconf to properly merge repo-specific settings with
        the app-level defaults, allowing repos to override only
        the settings they need to change.
    
        Args:
            toml_content: Raw TOML content from repo's stampbot.toml
    
        Returns:
            RepoConfig with merged settings
        """
        import toml
    
        # Start with defaults
>       defaults = cls._get_defaults()
                   ^^^^^^^^^^^^^^^^^^^
E       TypeError: RepoConfig._get_defaults() missing 1 required positional argument: 'cls'

stampbot/config.py:244: TypeError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_repo_config_from_toml - TypeError: RepoConfig._get_defaults() missing 1 required positional argument: 'cls'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 3 deselected in 0.39s
operator: core/RemoveDecorator, occurrence: 1
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -225,8 +225,6 @@
                     defaults[key] = getattr(settings_defaults, key)
 
         return defaults
-
-    @classmethod
     def from_toml(cls, toml_content: str) -> RepoConfig:
         """Parse TOML content and merge with app defaults.
 
F
=================================== FAILURES ===================================
__________________________ test_repo_config_from_toml __________________________

    def test_repo_config_from_toml():
        """Test parsing repository config from TOML."""
        toml_content = """
    approval_labels = ["test", "autoapprove"]
    auto_approve_on_label = true
    chatops_enabled = true
    chatops_required_permission = "write"
    approve_commands = ["approve", "stamp"]
    unapprove_commands = ["unapprove"]
    """
>       config = RepoConfig.from_toml(toml_content)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E       TypeError: RepoConfig.from_toml() missing 1 required positional argument: 'toml_content'

tests/test_config.py:20: TypeError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_repo_config_from_toml - TypeError: RepoConfig.from_toml() missing 1 required positional argument: 'toml_content'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 3 deselected in 0.39s
operator: core/RemoveDecorator, occurrence: 2
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -283,8 +283,6 @@
             allowed_users=merged.get("allowed_users", []),
             allowed_teams=merged.get("allowed_teams", []),
         )
-
-    @classmethod
     def default(cls) -> RepoConfig:
         """Return default configuration from app settings.
 
.F
=================================== FAILURES ===================================
___________________________ test_repo_config_default ___________________________

    def test_repo_config_default():
        """Test default repository config."""
>       config = RepoConfig.default()
                 ^^^^^^^^^^^^^^^^^^^^
E       TypeError: RepoConfig.default() missing 1 required positional argument: 'cls'

tests/test_config.py:32: TypeError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_repo_config_default - TypeError: RepoConfig.default() missing 1 required positional argument: 'cls'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 1 passed, 3 deselected in 0.42s
operator: core/ZeroIterationForLoop, occurrence: 0
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -175,7 +175,7 @@
 
             # Or if they're a member of an allowed team
             if self.allowed_teams and author_team_slugs:
-                for team in self.allowed_teams:
+                for team in []:
                     # Support both "org/team" and "team" formats
                     team_slug = team.split("/")[-1] if "/" in team else team
                     if team_slug in author_team_slugs:
..........................F
=================================== FAILURES ===================================
_______________________ test_is_pr_eligible_allowed_team _______________________

    def test_is_pr_eligible_allowed_team():
        """Test is_pr_eligible returns True when author is in an allowed team."""
        config = RepoConfig.from_toml('allowed_teams = ["my-org/release-team"]')
        is_eligible, reason = config.is_pr_eligible(
            [], "Update deps", "team-member", author_team_slugs=["release-team"]
        )
>       assert is_eligible is True
E       assert False is True

tests/test_config.py:311: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_is_pr_eligible_allowed_team - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 26 passed, 3 deselected in 0.45s
operator: core/ZeroIterationForLoop, occurrence: 1
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -220,7 +220,7 @@
         # Allow overriding defaults via settings.toml [defaults] section
         if settings.get("defaults"):
             settings_defaults = settings.defaults
-            for key in defaults:
+            for key in []:
                 if hasattr(settings_defaults, key):
                     defaults[key] = getattr(settings_defaults, key)
 
........F
=================================== FAILURES ===================================
_____________ test_repo_config_get_defaults_with_settings_override _____________

    def test_repo_config_get_defaults_with_settings_override():
        """Test _get_defaults respects settings.toml overrides."""
        with patch("stampbot.config.settings") as mock_settings:
            # Create a mock defaults object with only specific attributes
            # Using spec=[] means hasattr will return False for most attributes
            mock_defaults = MagicMock(
                spec=["approval_labels", "chatops_enabled", "chatops_required_permission"]
            )
            mock_defaults.approval_labels = ["custom-label"]
            mock_defaults.chatops_enabled = False
            mock_defaults.chatops_required_permission = "write"
    
            mock_settings.get.return_value = mock_defaults
            mock_settings.defaults = mock_defaults
    
            defaults = RepoConfig._get_defaults()
    
            # Should have the overridden values
>           assert defaults["approval_labels"] == ["custom-label"]
E           AssertionError: assert ['autoapprove', 'stamp'] == ['custom-label']
E             
E             At index 0 diff: 'autoapprove' != 'custom-label'
E             Left contains one more item: 'stamp'
E             
E             Full diff:
E               [
E             -     'custom-label',
E             +     'autoapprove',
E             +     'stamp',
E               ]

tests/test_config.py:122: AssertionError
=========================== short test summary info ============================
FAILED tests/test_config.py::test_repo_config_get_defaults_with_settings_override - AssertionError: assert ['autoapprove', 'stamp'] == ['custom-label']
  
  At index 0 diff: 'autoapprove' != 'custom-label'
  Left contains one more item: 'stamp'
  
  Full diff:
    [
  -     'custom-label',
  +     'autoapprove',
  +     'stamp',
    ]
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 8 passed, 3 deselected in 0.43s
operator: core/ZeroIterationForLoop, occurrence: 2
--- mutation diff ---
--- astampbot/config.py
+++ bstampbot/config.py
@@ -265,7 +265,7 @@
 
         # Validate regex patterns
         title_patterns = merged.get("required_title_patterns", [])
-        for pattern in title_patterns:
+        for pattern in []:
             try:
                 re.compile(pattern)
             except re.error as e:
.............F
=================================== FAILURES ===================================
____________________ test_repo_config_invalid_regex_pattern ____________________

    def test_repo_config_invalid_regex_pattern():
        """Test invalid regex pattern raises a ValueError."""
        toml_content = """
    required_title_patterns = ["[invalid"]
    """
>       with pytest.raises(ValueError, match="Invalid regex pattern"):
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E       Failed: DID NOT RAISE <class 'ValueError'>

tests/test_config.py:189: Failed
=========================== short test summary info ============================
FAILED tests/test_config.py::test_repo_config_invalid_regex_pattern - Failed: DID NOT RAISE <class 'ValueError'>
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 13 passed, 3 deselected in 0.44s
operator: core/ReplaceBinaryOperator_Add_Sub, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -219,7 +219,7 @@
         return RedirectResponse(url="/setup", status_code=307)
 
     return Response(
-        content='{"app": "stampbot", "version": "' + APP_VERSION + '", "status": "running"}',
+        content='{"app": "stampbot", "version": "' - APP_VERSION + '", "status": "running"}',
         media_type="application/json",
     )
 
........................................................................ [ 34%]
.....................F
=================================== FAILURES ===================================
______________________________ test_root_endpoint ______________________________

test_client = <starlette.testclient.TestClient object at 0x7fa9eb380050>

    def test_root_endpoint(test_client: TestClient):
        """Test root endpoint returns basic info."""
>       response = test_client.get("/")
                   ^^^^^^^^^^^^^^^^^^^^

tests/test_main.py:44: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:473: in get
    return super().get(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1053: in get
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    @app.get("/")
    async def root() -> Response:
        """Root endpoint - redirects to setup if not configured.
    
        Returns:
            Redirect to /setup if unconfigured, otherwise JSON status response.
        """
        if not is_configured() and settings.setup_enabled:
            return RedirectResponse(url="/setup", status_code=307)
    
        return Response(
>           content='{"app": "stampbot", "version": "' - APP_VERSION + '", "status": "running"}',
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            media_type="application/json",
        )
E       TypeError: unsupported operand type(s) for -: 'str' and 'str'

stampbot/main.py:222: TypeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_root_endpoint - TypeError: unsupported operand type(s) for -: 'str' and 'str'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 93 passed, 3 deselected in 1.16s
operator: core/ReplaceBinaryOperator_Add_Sub, occurrence: 1
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -219,7 +219,7 @@
         return RedirectResponse(url="/setup", status_code=307)
 
     return Response(
-        content='{"app": "stampbot", "version": "' + APP_VERSION + '", "status": "running"}',
+        content='{"app": "stampbot", "version": "' + APP_VERSION - '", "status": "running"}',
         media_type="application/json",
     )
 
........................................................................ [ 34%]
.....................F
=================================== FAILURES ===================================
______________________________ test_root_endpoint ______________________________

test_client = <starlette.testclient.TestClient object at 0x7efcdb690050>

    def test_root_endpoint(test_client: TestClient):
        """Test root endpoint returns basic info."""
>       response = test_client.get("/")
                   ^^^^^^^^^^^^^^^^^^^^

tests/test_main.py:44: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:473: in get
    return super().get(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1053: in get
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    @app.get("/")
    async def root() -> Response:
        """Root endpoint - redirects to setup if not configured.
    
        Returns:
            Redirect to /setup if unconfigured, otherwise JSON status response.
        """
        if not is_configured() and settings.setup_enabled:
            return RedirectResponse(url="/setup", status_code=307)
    
        return Response(
>           content='{"app": "stampbot", "version": "' + APP_VERSION - '", "status": "running"}',
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            media_type="application/json",
        )
E       TypeError: unsupported operand type(s) for -: 'str' and 'str'

stampbot/main.py:222: TypeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_root_endpoint - TypeError: unsupported operand type(s) for -: 'str' and 'str'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 93 passed, 3 deselected in 1.15s
operator: core/ReplaceBinaryOperator_Add_Mul, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -219,7 +219,7 @@
         return RedirectResponse(url="/setup", status_code=307)
 
     return Response(
-        content='{"app": "stampbot", "version": "' + APP_VERSION + '", "status": "running"}',
+        content='{"app": "stampbot", "version": "' * APP_VERSION + '", "status": "running"}',
         media_type="application/json",
     )
 
........................................................................ [ 34%]
.....................F
=================================== FAILURES ===================================
______________________________ test_root_endpoint ______________________________

test_client = <starlette.testclient.TestClient object at 0x7f1ef57a0050>

    def test_root_endpoint(test_client: TestClient):
        """Test root endpoint returns basic info."""
>       response = test_client.get("/")
                   ^^^^^^^^^^^^^^^^^^^^

tests/test_main.py:44: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:473: in get
    return super().get(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1053: in get
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:450: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    @app.get("/")
    async def root() -> Response:
        """Root endpoint - redirects to setup if not configured.
    
        Returns:
            Redirect to /setup if unconfigured, otherwise JSON status response.
        """
        if not is_configured() and settings.setup_enabled:
            return RedirectResponse(url="/setup", status_code=307)
    
        return Response(
>           content='{"app": "stampbot", "version": "' * APP_VERSION + '", "status": "running"}',
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            media_type="application/json",
        )
E       TypeError: can't multiply sequence by non-int of type 'str'

stampbot/main.py:222: TypeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_root_endpoint - TypeError: can't multiply sequence by non-int of type 'str'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 93 passed, 3 deselected in 1.15s
operator: core/ReplaceBinaryOperator_Add_Mul, occurrence: 1
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -219,7 +219,7 @@
         return RedirectResponse(url="/setup", status_code=307)
 
     return Response(
-        content='{"app": "stampbot", "version": "' + APP_VERSION + '", "status": "running"}',
+        content='{"app": "stampbot", "version": "' + APP_VERSION * '", "status": "running"}',
         media_type="application/json",
     )
 
........................................................................ [ 34%]
.....................F
=================================== FAILURES ===================================
______________________________ test_root_endpoint ______________________________

test_client = <starlette.testclient.TestClient object at 0x7f70082a4050>

    def test_root_endpoint(test_client: TestClient):
        """Test root endpoint returns basic info."""
>       response = test_client.get("/")
                   ^^^^^^^^^^^^^^^^^^^^

tests/test_main.py:44: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:473: in get
    return super().get(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1053: in get
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:450: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    @app.get("/")
    async def root() -> Response:
        """Root endpoint - redirects to setup if not configured.
    
        Returns:
            Redirect to /setup if unconfigured, otherwise JSON status response.
        """
        if not is_configured() and settings.setup_enabled:
            return RedirectResponse(url="/setup", status_code=307)
    
        return Response(
>           content='{"app": "stampbot", "version": "' + APP_VERSION * '", "status": "running"}',
                                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            media_type="application/json",
        )
E       TypeError: can't multiply sequence by non-int of type 'str'

stampbot/main.py:222: TypeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_root_endpoint - TypeError: can't multiply sequence by non-int of type 'str'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 93 passed, 3 deselected in 1.16s
operator: core/ReplaceBinaryOperator_Add_Div, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -219,7 +219,7 @@
         return RedirectResponse(url="/setup", status_code=307)
 
     return Response(
-        content='{"app": "stampbot", "version": "' + APP_VERSION + '", "status": "running"}',
+        content='{"app": "stampbot", "version": "' / APP_VERSION + '", "status": "running"}',
         media_type="application/json",
     )
 
........................................................................ [ 34%]
.....................F
=================================== FAILURES ===================================
______________________________ test_root_endpoint ______________________________

test_client = <starlette.testclient.TestClient object at 0x7f4cacea4050>

    def test_root_endpoint(test_client: TestClient):
        """Test root endpoint returns basic info."""
>       response = test_client.get("/")
                   ^^^^^^^^^^^^^^^^^^^^

tests/test_main.py:44: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:473: in get
    return super().get(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1053: in get
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:450: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    @app.get("/")
    async def root() -> Response:
        """Root endpoint - redirects to setup if not configured.
    
        Returns:
            Redirect to /setup if unconfigured, otherwise JSON status response.
        """
        if not is_configured() and settings.setup_enabled:
            return RedirectResponse(url="/setup", status_code=307)
    
        return Response(
>           content='{"app": "stampbot", "version": "' / APP_VERSION + '", "status": "running"}',
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            media_type="application/json",
        )
E       TypeError: unsupported operand type(s) for /: 'str' and 'str'

stampbot/main.py:222: TypeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_root_endpoint - TypeError: unsupported operand type(s) for /: 'str' and 'str'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 93 passed, 3 deselected in 1.15s
operator: core/ReplaceBinaryOperator_Add_Div, occurrence: 1
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -219,7 +219,7 @@
         return RedirectResponse(url="/setup", status_code=307)
 
     return Response(
-        content='{"app": "stampbot", "version": "' + APP_VERSION + '", "status": "running"}',
+        content='{"app": "stampbot", "version": "' + APP_VERSION / '", "status": "running"}',
         media_type="application/json",
     )
 
........................................................................ [ 34%]
.....................F
=================================== FAILURES ===================================
______________________________ test_root_endpoint ______________________________

test_client = <starlette.testclient.TestClient object at 0x7f1eb6cbc050>

    def test_root_endpoint(test_client: TestClient):
        """Test root endpoint returns basic info."""
>       response = test_client.get("/")
                   ^^^^^^^^^^^^^^^^^^^^

tests/test_main.py:44: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:473: in get
    return super().get(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1053: in get
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    @app.get("/")
    async def root() -> Response:
        """Root endpoint - redirects to setup if not configured.
    
        Returns:
            Redirect to /setup if unconfigured, otherwise JSON status response.
        """
        if not is_configured() and settings.setup_enabled:
            return RedirectResponse(url="/setup", status_code=307)
    
        return Response(
>           content='{"app": "stampbot", "version": "' + APP_VERSION / '", "status": "running"}',
                                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            media_type="application/json",
        )
E       TypeError: unsupported operand type(s) for /: 'str' and 'str'

stampbot/main.py:222: TypeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_root_endpoint - TypeError: unsupported operand type(s) for /: 'str' and 'str'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 93 passed, 3 deselected in 1.16s
operator: core/ReplaceBinaryOperator_Add_FloorDiv, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -219,7 +219,7 @@
         return RedirectResponse(url="/setup", status_code=307)
 
     return Response(
-        content='{"app": "stampbot", "version": "' + APP_VERSION + '", "status": "running"}',
+        content='{"app": "stampbot", "version": "' // APP_VERSION + '", "status": "running"}',
         media_type="application/json",
     )
 
........................................................................ [ 34%]
.....................F
=================================== FAILURES ===================================
______________________________ test_root_endpoint ______________________________

test_client = <starlette.testclient.TestClient object at 0x7fca9ba94050>

    def test_root_endpoint(test_client: TestClient):
        """Test root endpoint returns basic info."""
>       response = test_client.get("/")
                   ^^^^^^^^^^^^^^^^^^^^

tests/test_main.py:44: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:473: in get
    return super().get(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1053: in get
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    @app.get("/")
    async def root() -> Response:
        """Root endpoint - redirects to setup if not configured.
    
        Returns:
            Redirect to /setup if unconfigured, otherwise JSON status response.
        """
        if not is_configured() and settings.setup_enabled:
            return RedirectResponse(url="/setup", status_code=307)
    
        return Response(
>           content='{"app": "stampbot", "version": "' // APP_VERSION + '", "status": "running"}',
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            media_type="application/json",
        )
E       TypeError: unsupported operand type(s) for //: 'str' and 'str'

stampbot/main.py:222: TypeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_root_endpoint - TypeError: unsupported operand type(s) for //: 'str' and 'str'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 93 passed, 3 deselected in 1.15s
operator: core/ReplaceBinaryOperator_Add_FloorDiv, occurrence: 1
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -219,7 +219,7 @@
         return RedirectResponse(url="/setup", status_code=307)
 
     return Response(
-        content='{"app": "stampbot", "version": "' + APP_VERSION + '", "status": "running"}',
+        content='{"app": "stampbot", "version": "' + APP_VERSION // '", "status": "running"}',
         media_type="application/json",
     )
 
........................................................................ [ 34%]
.....................F
=================================== FAILURES ===================================
______________________________ test_root_endpoint ______________________________

test_client = <starlette.testclient.TestClient object at 0x7fac06280050>

    def test_root_endpoint(test_client: TestClient):
        """Test root endpoint returns basic info."""
>       response = test_client.get("/")
                   ^^^^^^^^^^^^^^^^^^^^

tests/test_main.py:44: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:473: in get
    return super().get(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1053: in get
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    @app.get("/")
    async def root() -> Response:
        """Root endpoint - redirects to setup if not configured.
    
        Returns:
            Redirect to /setup if unconfigured, otherwise JSON status response.
        """
        if not is_configured() and settings.setup_enabled:
            return RedirectResponse(url="/setup", status_code=307)
    
        return Response(
>           content='{"app": "stampbot", "version": "' + APP_VERSION // '", "status": "running"}',
                                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            media_type="application/json",
        )
E       TypeError: unsupported operand type(s) for //: 'str' and 'str'

stampbot/main.py:222: TypeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_root_endpoint - TypeError: unsupported operand type(s) for //: 'str' and 'str'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 93 passed, 3 deselected in 1.15s
operator: core/ReplaceBinaryOperator_Add_Mod, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -219,7 +219,7 @@
         return RedirectResponse(url="/setup", status_code=307)
 
     return Response(
-        content='{"app": "stampbot", "version": "' + APP_VERSION + '", "status": "running"}',
+        content='{"app": "stampbot", "version": "' % APP_VERSION + '", "status": "running"}',
         media_type="application/json",
     )
 
........................................................................ [ 34%]
.....................F
=================================== FAILURES ===================================
______________________________ test_root_endpoint ______________________________

test_client = <starlette.testclient.TestClient object at 0x7f260a798050>

    def test_root_endpoint(test_client: TestClient):
        """Test root endpoint returns basic info."""
>       response = test_client.get("/")
                   ^^^^^^^^^^^^^^^^^^^^

tests/test_main.py:44: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:473: in get
    return super().get(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1053: in get
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:450: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    @app.get("/")
    async def root() -> Response:
        """Root endpoint - redirects to setup if not configured.
    
        Returns:
            Redirect to /setup if unconfigured, otherwise JSON status response.
        """
        if not is_configured() and settings.setup_enabled:
            return RedirectResponse(url="/setup", status_code=307)
    
        return Response(
>           content='{"app": "stampbot", "version": "' % APP_VERSION + '", "status": "running"}',
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            media_type="application/json",
        )
E       TypeError: not all arguments converted during string formatting

stampbot/main.py:222: TypeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_root_endpoint - TypeError: not all arguments converted during string formatting
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 93 passed, 3 deselected in 1.16s
operator: core/ReplaceBinaryOperator_Add_Mod, occurrence: 1
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -219,7 +219,7 @@
         return RedirectResponse(url="/setup", status_code=307)
 
     return Response(
-        content='{"app": "stampbot", "version": "' + APP_VERSION + '", "status": "running"}',
+        content='{"app": "stampbot", "version": "' + APP_VERSION % '", "status": "running"}',
         media_type="application/json",
     )
 
........................................................................ [ 34%]
.....................F
=================================== FAILURES ===================================
______________________________ test_root_endpoint ______________________________

test_client = <starlette.testclient.TestClient object at 0x7f38a318c050>

    def test_root_endpoint(test_client: TestClient):
        """Test root endpoint returns basic info."""
>       response = test_client.get("/")
                   ^^^^^^^^^^^^^^^^^^^^

tests/test_main.py:44: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:473: in get
    return super().get(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1053: in get
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    @app.get("/")
    async def root() -> Response:
        """Root endpoint - redirects to setup if not configured.
    
        Returns:
            Redirect to /setup if unconfigured, otherwise JSON status response.
        """
        if not is_configured() and settings.setup_enabled:
            return RedirectResponse(url="/setup", status_code=307)
    
        return Response(
>           content='{"app": "stampbot", "version": "' + APP_VERSION % '", "status": "running"}',
                                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            media_type="application/json",
        )
E       TypeError: not all arguments converted during string formatting

stampbot/main.py:222: TypeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_root_endpoint - TypeError: not all arguments converted during string formatting
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 93 passed, 3 deselected in 1.15s
operator: core/ReplaceBinaryOperator_Add_Pow, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -219,7 +219,7 @@
         return RedirectResponse(url="/setup", status_code=307)
 
     return Response(
-        content='{"app": "stampbot", "version": "' + APP_VERSION + '", "status": "running"}',
+        content='{"app": "stampbot", "version": "' ** APP_VERSION + '", "status": "running"}',
         media_type="application/json",
     )
 
........................................................................ [ 34%]
.....................F
=================================== FAILURES ===================================
______________________________ test_root_endpoint ______________________________

test_client = <starlette.testclient.TestClient object at 0x7f87dc79c050>

    def test_root_endpoint(test_client: TestClient):
        """Test root endpoint returns basic info."""
>       response = test_client.get("/")
                   ^^^^^^^^^^^^^^^^^^^^

tests/test_main.py:44: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:473: in get
    return super().get(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1053: in get
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    @app.get("/")
    async def root() -> Response:
        """Root endpoint - redirects to setup if not configured.
    
        Returns:
            Redirect to /setup if unconfigured, otherwise JSON status response.
        """
        if not is_configured() and settings.setup_enabled:
            return RedirectResponse(url="/setup", status_code=307)
    
        return Response(
>           content='{"app": "stampbot", "version": "' ** APP_VERSION + '", "status": "running"}',
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            media_type="application/json",
        )
E       TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'str'

stampbot/main.py:222: TypeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_root_endpoint - TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'str'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 93 passed, 3 deselected in 1.14s
operator: core/ReplaceBinaryOperator_Add_Pow, occurrence: 1
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -219,7 +219,7 @@
         return RedirectResponse(url="/setup", status_code=307)
 
     return Response(
-        content='{"app": "stampbot", "version": "' + APP_VERSION + '", "status": "running"}',
+        content='{"app": "stampbot", "version": "' + APP_VERSION ** '", "status": "running"}',
         media_type="application/json",
     )
 
........................................................................ [ 34%]
.....................F
=================================== FAILURES ===================================
______________________________ test_root_endpoint ______________________________

test_client = <starlette.testclient.TestClient object at 0x7fe7965a4050>

    def test_root_endpoint(test_client: TestClient):
        """Test root endpoint returns basic info."""
>       response = test_client.get("/")
                   ^^^^^^^^^^^^^^^^^^^^

tests/test_main.py:44: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:473: in get
    return super().get(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1053: in get
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    @app.get("/")
    async def root() -> Response:
        """Root endpoint - redirects to setup if not configured.
    
        Returns:
            Redirect to /setup if unconfigured, otherwise JSON status response.
        """
        if not is_configured() and settings.setup_enabled:
            return RedirectResponse(url="/setup", status_code=307)
    
        return Response(
>           content='{"app": "stampbot", "version": "' + APP_VERSION ** '", "status": "running"}',
                                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            media_type="application/json",
        )
E       TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'str'

stampbot/main.py:222: TypeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_root_endpoint - TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'str'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 93 passed, 3 deselected in 1.18s
operator: core/ReplaceBinaryOperator_Add_RShift, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -219,7 +219,7 @@
         return RedirectResponse(url="/setup", status_code=307)
 
     return Response(
-        content='{"app": "stampbot", "version": "' + APP_VERSION + '", "status": "running"}',
+        content='{"app": "stampbot", "version": "' >> APP_VERSION + '", "status": "running"}',
         media_type="application/json",
     )
 
........................................................................ [ 34%]
.....................F
=================================== FAILURES ===================================
______________________________ test_root_endpoint ______________________________

test_client = <starlette.testclient.TestClient object at 0x7f16e2f98050>

    def test_root_endpoint(test_client: TestClient):
        """Test root endpoint returns basic info."""
>       response = test_client.get("/")
                   ^^^^^^^^^^^^^^^^^^^^

tests/test_main.py:44: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:473: in get
    return super().get(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1053: in get
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    @app.get("/")
    async def root() -> Response:
        """Root endpoint - redirects to setup if not configured.
    
        Returns:
            Redirect to /setup if unconfigured, otherwise JSON status response.
        """
        if not is_configured() and settings.setup_enabled:
            return RedirectResponse(url="/setup", status_code=307)
    
        return Response(
>           content='{"app": "stampbot", "version": "' >> APP_VERSION + '", "status": "running"}',
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            media_type="application/json",
        )
E       TypeError: unsupported operand type(s) for >>: 'str' and 'str'

stampbot/main.py:222: TypeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_root_endpoint - TypeError: unsupported operand type(s) for >>: 'str' and 'str'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 93 passed, 3 deselected in 1.15s
operator: core/ReplaceBinaryOperator_Add_RShift, occurrence: 1
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -219,7 +219,7 @@
         return RedirectResponse(url="/setup", status_code=307)
 
     return Response(
-        content='{"app": "stampbot", "version": "' + APP_VERSION + '", "status": "running"}',
+        content='{"app": "stampbot", "version": "' + APP_VERSION >> '", "status": "running"}',
         media_type="application/json",
     )
 
........................................................................ [ 34%]
.....................F
=================================== FAILURES ===================================
______________________________ test_root_endpoint ______________________________

test_client = <starlette.testclient.TestClient object at 0x7f59c4998050>

    def test_root_endpoint(test_client: TestClient):
        """Test root endpoint returns basic info."""
>       response = test_client.get("/")
                   ^^^^^^^^^^^^^^^^^^^^

tests/test_main.py:44: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:473: in get
    return super().get(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1053: in get
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:450: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    @app.get("/")
    async def root() -> Response:
        """Root endpoint - redirects to setup if not configured.
    
        Returns:
            Redirect to /setup if unconfigured, otherwise JSON status response.
        """
        if not is_configured() and settings.setup_enabled:
            return RedirectResponse(url="/setup", status_code=307)
    
        return Response(
>           content='{"app": "stampbot", "version": "' + APP_VERSION >> '", "status": "running"}',
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            media_type="application/json",
        )
E       TypeError: unsupported operand type(s) for >>: 'str' and 'str'

stampbot/main.py:222: TypeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_root_endpoint - TypeError: unsupported operand type(s) for >>: 'str' and 'str'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 93 passed, 3 deselected in 1.28s
operator: core/ReplaceBinaryOperator_Add_LShift, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -219,7 +219,7 @@
         return RedirectResponse(url="/setup", status_code=307)
 
     return Response(
-        content='{"app": "stampbot", "version": "' + APP_VERSION + '", "status": "running"}',
+        content='{"app": "stampbot", "version": "' << APP_VERSION + '", "status": "running"}',
         media_type="application/json",
     )
 
........................................................................ [ 34%]
.....................F
=================================== FAILURES ===================================
______________________________ test_root_endpoint ______________________________

test_client = <starlette.testclient.TestClient object at 0x7fa1d7388050>

    def test_root_endpoint(test_client: TestClient):
        """Test root endpoint returns basic info."""
>       response = test_client.get("/")
                   ^^^^^^^^^^^^^^^^^^^^

tests/test_main.py:44: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:473: in get
    return super().get(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1053: in get
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    @app.get("/")
    async def root() -> Response:
        """Root endpoint - redirects to setup if not configured.
    
        Returns:
            Redirect to /setup if unconfigured, otherwise JSON status response.
        """
        if not is_configured() and settings.setup_enabled:
            return RedirectResponse(url="/setup", status_code=307)
    
        return Response(
>           content='{"app": "stampbot", "version": "' << APP_VERSION + '", "status": "running"}',
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            media_type="application/json",
        )
E       TypeError: unsupported operand type(s) for <<: 'str' and 'str'

stampbot/main.py:222: TypeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_root_endpoint - TypeError: unsupported operand type(s) for <<: 'str' and 'str'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 93 passed, 3 deselected in 1.16s
operator: core/ReplaceBinaryOperator_Add_LShift, occurrence: 1
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -219,7 +219,7 @@
         return RedirectResponse(url="/setup", status_code=307)
 
     return Response(
-        content='{"app": "stampbot", "version": "' + APP_VERSION + '", "status": "running"}',
+        content='{"app": "stampbot", "version": "' + APP_VERSION << '", "status": "running"}',
         media_type="application/json",
     )
 
........................................................................ [ 34%]
.....................F
=================================== FAILURES ===================================
______________________________ test_root_endpoint ______________________________

test_client = <starlette.testclient.TestClient object at 0x7f638b974050>

    def test_root_endpoint(test_client: TestClient):
        """Test root endpoint returns basic info."""
>       response = test_client.get("/")
                   ^^^^^^^^^^^^^^^^^^^^

tests/test_main.py:44: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:473: in get
    return super().get(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1053: in get
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:450: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    @app.get("/")
    async def root() -> Response:
        """Root endpoint - redirects to setup if not configured.
    
        Returns:
            Redirect to /setup if unconfigured, otherwise JSON status response.
        """
        if not is_configured() and settings.setup_enabled:
            return RedirectResponse(url="/setup", status_code=307)
    
        return Response(
>           content='{"app": "stampbot", "version": "' + APP_VERSION << '", "status": "running"}',
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            media_type="application/json",
        )
E       TypeError: unsupported operand type(s) for <<: 'str' and 'str'

stampbot/main.py:222: TypeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_root_endpoint - TypeError: unsupported operand type(s) for <<: 'str' and 'str'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 93 passed, 3 deselected in 1.17s
operator: core/ReplaceBinaryOperator_Add_BitOr, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -219,7 +219,7 @@
         return RedirectResponse(url="/setup", status_code=307)
 
     return Response(
-        content='{"app": "stampbot", "version": "' + APP_VERSION + '", "status": "running"}',
+        content='{"app": "stampbot", "version": "' | APP_VERSION + '", "status": "running"}',
         media_type="application/json",
     )
 
........................................................................ [ 34%]
.....................F
=================================== FAILURES ===================================
______________________________ test_root_endpoint ______________________________

test_client = <starlette.testclient.TestClient object at 0x7fd29d984050>

    def test_root_endpoint(test_client: TestClient):
        """Test root endpoint returns basic info."""
>       response = test_client.get("/")
                   ^^^^^^^^^^^^^^^^^^^^

tests/test_main.py:44: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:473: in get
    return super().get(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1053: in get
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    @app.get("/")
    async def root() -> Response:
        """Root endpoint - redirects to setup if not configured.
    
        Returns:
            Redirect to /setup if unconfigured, otherwise JSON status response.
        """
        if not is_configured() and settings.setup_enabled:
            return RedirectResponse(url="/setup", status_code=307)
    
        return Response(
>           content='{"app": "stampbot", "version": "' | APP_VERSION + '", "status": "running"}',
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            media_type="application/json",
        )
E       TypeError: unsupported operand type(s) for |: 'str' and 'str'

stampbot/main.py:222: TypeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_root_endpoint - TypeError: unsupported operand type(s) for |: 'str' and 'str'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 93 passed, 3 deselected in 1.16s
operator: core/ReplaceBinaryOperator_Add_BitOr, occurrence: 1
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -219,7 +219,7 @@
         return RedirectResponse(url="/setup", status_code=307)
 
     return Response(
-        content='{"app": "stampbot", "version": "' + APP_VERSION + '", "status": "running"}',
+        content='{"app": "stampbot", "version": "' + APP_VERSION | '", "status": "running"}',
         media_type="application/json",
     )
 
........................................................................ [ 34%]
.....................F
=================================== FAILURES ===================================
______________________________ test_root_endpoint ______________________________

test_client = <starlette.testclient.TestClient object at 0x7f4eac9a4050>

    def test_root_endpoint(test_client: TestClient):
        """Test root endpoint returns basic info."""
>       response = test_client.get("/")
                   ^^^^^^^^^^^^^^^^^^^^

tests/test_main.py:44: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:473: in get
    return super().get(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1053: in get
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    @app.get("/")
    async def root() -> Response:
        """Root endpoint - redirects to setup if not configured.
    
        Returns:
            Redirect to /setup if unconfigured, otherwise JSON status response.
        """
        if not is_configured() and settings.setup_enabled:
            return RedirectResponse(url="/setup", status_code=307)
    
        return Response(
>           content='{"app": "stampbot", "version": "' + APP_VERSION | '", "status": "running"}',
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            media_type="application/json",
        )
E       TypeError: unsupported operand type(s) for |: 'str' and 'str'

stampbot/main.py:222: TypeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_root_endpoint - TypeError: unsupported operand type(s) for |: 'str' and 'str'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 93 passed, 3 deselected in 1.16s
operator: core/ReplaceBinaryOperator_Add_BitAnd, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -219,7 +219,7 @@
         return RedirectResponse(url="/setup", status_code=307)
 
     return Response(
-        content='{"app": "stampbot", "version": "' + APP_VERSION + '", "status": "running"}',
+        content='{"app": "stampbot", "version": "' & APP_VERSION + '", "status": "running"}',
         media_type="application/json",
     )
 
........................................................................ [ 34%]
.....................F
=================================== FAILURES ===================================
______________________________ test_root_endpoint ______________________________

test_client = <starlette.testclient.TestClient object at 0x7fb2065a4050>

    def test_root_endpoint(test_client: TestClient):
        """Test root endpoint returns basic info."""
>       response = test_client.get("/")
                   ^^^^^^^^^^^^^^^^^^^^

tests/test_main.py:44: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:473: in get
    return super().get(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1053: in get
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    @app.get("/")
    async def root() -> Response:
        """Root endpoint - redirects to setup if not configured.
    
        Returns:
            Redirect to /setup if unconfigured, otherwise JSON status response.
        """
        if not is_configured() and settings.setup_enabled:
            return RedirectResponse(url="/setup", status_code=307)
    
        return Response(
>           content='{"app": "stampbot", "version": "' & APP_VERSION + '", "status": "running"}',
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            media_type="application/json",
        )
E       TypeError: unsupported operand type(s) for &: 'str' and 'str'

stampbot/main.py:222: TypeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_root_endpoint - TypeError: unsupported operand type(s) for &: 'str' and 'str'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 93 passed, 3 deselected in 1.16s
operator: core/ReplaceBinaryOperator_Add_BitAnd, occurrence: 1
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -219,7 +219,7 @@
         return RedirectResponse(url="/setup", status_code=307)
 
     return Response(
-        content='{"app": "stampbot", "version": "' + APP_VERSION + '", "status": "running"}',
+        content='{"app": "stampbot", "version": "' + APP_VERSION & '", "status": "running"}',
         media_type="application/json",
     )
 
........................................................................ [ 34%]
.....................F
=================================== FAILURES ===================================
______________________________ test_root_endpoint ______________________________

test_client = <starlette.testclient.TestClient object at 0x7f8199aa0050>

    def test_root_endpoint(test_client: TestClient):
        """Test root endpoint returns basic info."""
>       response = test_client.get("/")
                   ^^^^^^^^^^^^^^^^^^^^

tests/test_main.py:44: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:473: in get
    return super().get(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1053: in get
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    @app.get("/")
    async def root() -> Response:
        """Root endpoint - redirects to setup if not configured.
    
        Returns:
            Redirect to /setup if unconfigured, otherwise JSON status response.
        """
        if not is_configured() and settings.setup_enabled:
            return RedirectResponse(url="/setup", status_code=307)
    
        return Response(
>           content='{"app": "stampbot", "version": "' + APP_VERSION & '", "status": "running"}',
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            media_type="application/json",
        )
E       TypeError: unsupported operand type(s) for &: 'str' and 'str'

stampbot/main.py:222: TypeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_root_endpoint - TypeError: unsupported operand type(s) for &: 'str' and 'str'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 93 passed, 3 deselected in 1.15s
operator: core/ReplaceBinaryOperator_Add_BitXor, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -219,7 +219,7 @@
         return RedirectResponse(url="/setup", status_code=307)
 
     return Response(
-        content='{"app": "stampbot", "version": "' + APP_VERSION + '", "status": "running"}',
+        content='{"app": "stampbot", "version": "' ^ APP_VERSION + '", "status": "running"}',
         media_type="application/json",
     )
 
........................................................................ [ 34%]
.....................F
=================================== FAILURES ===================================
______________________________ test_root_endpoint ______________________________

test_client = <starlette.testclient.TestClient object at 0x7f3479584050>

    def test_root_endpoint(test_client: TestClient):
        """Test root endpoint returns basic info."""
>       response = test_client.get("/")
                   ^^^^^^^^^^^^^^^^^^^^

tests/test_main.py:44: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:473: in get
    return super().get(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1053: in get
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    @app.get("/")
    async def root() -> Response:
        """Root endpoint - redirects to setup if not configured.
    
        Returns:
            Redirect to /setup if unconfigured, otherwise JSON status response.
        """
        if not is_configured() and settings.setup_enabled:
            return RedirectResponse(url="/setup", status_code=307)
    
        return Response(
>           content='{"app": "stampbot", "version": "' ^ APP_VERSION + '", "status": "running"}',
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            media_type="application/json",
        )
E       TypeError: unsupported operand type(s) for ^: 'str' and 'str'

stampbot/main.py:222: TypeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_root_endpoint - TypeError: unsupported operand type(s) for ^: 'str' and 'str'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 93 passed, 3 deselected in 1.14s
operator: core/ReplaceBinaryOperator_Add_BitXor, occurrence: 1
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -219,7 +219,7 @@
         return RedirectResponse(url="/setup", status_code=307)
 
     return Response(
-        content='{"app": "stampbot", "version": "' + APP_VERSION + '", "status": "running"}',
+        content='{"app": "stampbot", "version": "' + APP_VERSION ^ '", "status": "running"}',
         media_type="application/json",
     )
 
........................................................................ [ 34%]
.....................F
=================================== FAILURES ===================================
______________________________ test_root_endpoint ______________________________

test_client = <starlette.testclient.TestClient object at 0x7fa8a43a4050>

    def test_root_endpoint(test_client: TestClient):
        """Test root endpoint returns basic info."""
>       response = test_client.get("/")
                   ^^^^^^^^^^^^^^^^^^^^

tests/test_main.py:44: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:473: in get
    return super().get(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1053: in get
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    @app.get("/")
    async def root() -> Response:
        """Root endpoint - redirects to setup if not configured.
    
        Returns:
            Redirect to /setup if unconfigured, otherwise JSON status response.
        """
        if not is_configured() and settings.setup_enabled:
            return RedirectResponse(url="/setup", status_code=307)
    
        return Response(
>           content='{"app": "stampbot", "version": "' + APP_VERSION ^ '", "status": "running"}',
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            media_type="application/json",
        )
E       TypeError: unsupported operand type(s) for ^: 'str' and 'str'

stampbot/main.py:222: TypeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_root_endpoint - TypeError: unsupported operand type(s) for ^: 'str' and 'str'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 93 passed, 3 deselected in 1.16s
operator: core/ReplaceBinaryOperator_Sub_Add, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -119,7 +119,7 @@
     try:
         response = await call_next(request)
 
-        duration = time.time() - start_time
+        duration = time.time() + start_time
 
         # Track request metrics
         http_requests_total.labels(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.64s
operator: core/ReplaceBinaryOperator_Sub_Add, occurrence: 1
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -313,7 +313,7 @@
     try:
         start_time = time.time()
         result = await webhook_handler.handle_event(x_github_event, payload)
-        duration = time.time() - start_time
+        duration = time.time() + start_time
 
         webhook_processing_duration_seconds.labels(event_type=x_github_event or "unknown").observe(
             duration
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Mul, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -119,7 +119,7 @@
     try:
         response = await call_next(request)
 
-        duration = time.time() - start_time
+        duration = time.time() * start_time
 
         # Track request metrics
         http_requests_total.labels(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_Mul, occurrence: 1
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -313,7 +313,7 @@
     try:
         start_time = time.time()
         result = await webhook_handler.handle_event(x_github_event, payload)
-        duration = time.time() - start_time
+        duration = time.time() * start_time
 
         webhook_processing_duration_seconds.labels(event_type=x_github_event or "unknown").observe(
             duration
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.64s
operator: core/ReplaceBinaryOperator_Sub_Div, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -119,7 +119,7 @@
     try:
         response = await call_next(request)
 
-        duration = time.time() - start_time
+        duration = time.time() / start_time
 
         # Track request metrics
         http_requests_total.labels(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_Div, occurrence: 1
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -313,7 +313,7 @@
     try:
         start_time = time.time()
         result = await webhook_handler.handle_event(x_github_event, payload)
-        duration = time.time() - start_time
+        duration = time.time() / start_time
 
         webhook_processing_duration_seconds.labels(event_type=x_github_event or "unknown").observe(
             duration
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_FloorDiv, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -119,7 +119,7 @@
     try:
         response = await call_next(request)
 
-        duration = time.time() - start_time
+        duration = time.time() // start_time
 
         # Track request metrics
         http_requests_total.labels(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_FloorDiv, occurrence: 1
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -313,7 +313,7 @@
     try:
         start_time = time.time()
         result = await webhook_handler.handle_event(x_github_event, payload)
-        duration = time.time() - start_time
+        duration = time.time() // start_time
 
         webhook_processing_duration_seconds.labels(event_type=x_github_event or "unknown").observe(
             duration
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Mod, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -119,7 +119,7 @@
     try:
         response = await call_next(request)
 
-        duration = time.time() - start_time
+        duration = time.time() % start_time
 
         # Track request metrics
         http_requests_total.labels(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/ReplaceBinaryOperator_Sub_Mod, occurrence: 1
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -313,7 +313,7 @@
     try:
         start_time = time.time()
         result = await webhook_handler.handle_event(x_github_event, payload)
-        duration = time.time() - start_time
+        duration = time.time() % start_time
 
         webhook_processing_duration_seconds.labels(event_type=x_github_event or "unknown").observe(
             duration
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_Pow, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -119,7 +119,7 @@
     try:
         response = await call_next(request)
 
-        duration = time.time() - start_time
+        duration = time.time() ** start_time
 
         # Track request metrics
         http_requests_total.labels(
........................................................................ [ 34%]
...................F
=================================== FAILURES ===================================
__________________ test_lifespan_startup_shutdown_configured ___________________
  + Exception Group Traceback (most recent call last):
  |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py", line 79, in collapse_excgroups
  |     yield
  |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py", line 192, in __call__
  |     async with anyio.create_task_group() as task_group:
  |                ~~~~~~~~~~~~~~~~~~~~~~~^^
  |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/_backends/_asyncio.py", line 783, in __aexit__
  |     raise BaseExceptionGroup(
  |         "unhandled errors in a TaskGroup", self._exceptions
  |     ) from None
  | ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
  +-+---------------- 1 ----------------
    | Traceback (most recent call last):
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/runner.py", line 353, in from_call
    |     result: TResult | None = func()
    |                              ~~~~^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/runner.py", line 245, in <lambda>
    |     lambda: runtest_hook(item=item, **kwds),
    |             ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_hooks.py", line 512, in __call__
    |     return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
    |            ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_manager.py", line 120, in _hookexec
    |     return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
    |            ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 167, in _multicall
    |     raise exception
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 139, in _multicall
    |     teardown.throw(exception)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/logging.py", line 850, in pytest_runtest_call
    |     yield
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 139, in _multicall
    |     teardown.throw(exception)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 53, in run_old_style_hookwrapper
    |     return result.get_result()
    |            ~~~~~~~~~~~~~~~~~^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_result.py", line 103, in get_result
    |     raise exc.with_traceback(tb)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 38, in run_old_style_hookwrapper
    |     res = yield
    |           ^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 139, in _multicall
    |     teardown.throw(exception)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/capture.py", line 900, in pytest_runtest_call
    |     return (yield)
    |             ^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 139, in _multicall
    |     teardown.throw(exception)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/skipping.py", line 268, in pytest_runtest_call
    |     return (yield)
    |             ^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 121, in _multicall
    |     res = hook_impl.function(*args)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/runner.py", line 179, in pytest_runtest_call
    |     item.runtest()
    |     ~~~~~~~~~~~~^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/python.py", line 1720, in runtest
    |     self.ihook.pytest_pyfunc_call(pyfuncitem=self)
    |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_hooks.py", line 512, in __call__
    |     return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
    |            ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_manager.py", line 120, in _hookexec
    |     return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
    |            ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 167, in _multicall
    |     raise exception
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 139, in _multicall
    |     teardown.throw(exception)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 53, in run_old_style_hookwrapper
    |     return result.get_result()
    |            ~~~~~~~~~~~~~~~~~^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_result.py", line 103, in get_result
    |     raise exc.with_traceback(tb)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 38, in run_old_style_hookwrapper
    |     res = yield
    |           ^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 121, in _multicall
    |     res = hook_impl.function(*args)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/python.py", line 166, in pytest_pyfunc_call
    |     result = testfunction(**testargs)
    |   File "/home/runner/work/stampbot/stampbot/tests/test_main.py", line 20, in test_lifespan_startup_shutdown_configured
    |     response = client.get("/health")
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 473, in get
    |     return super().get(
    |            ~~~~~~~~~~~^
    |         url,
    |         ^^^^
    |     ...<6 lines>...
    |         extensions=extensions,
    |         ^^^^^^^^^^^^^^^^^^^^^^
    |     )
    |     ^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 1053, in get
    |     return self.request(
    |            ~~~~~~~~~~~~^
    |         "GET",
    |         ^^^^^^
    |     ...<7 lines>...
    |         extensions=extensions,
    |         ^^^^^^^^^^^^^^^^^^^^^^
    |     )
    |     ^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 445, in request
    |     return super().request(
    |            ~~~~~~~~~~~~~~~^
    |         method,
    |         ^^^^^^^
    |     ...<11 lines>...
    |         extensions=extensions,
    |         ^^^^^^^^^^^^^^^^^^^^^^
    |     )
    |     ^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 825, in request
    |     return self.send(request, auth=auth, follow_redirects=follow_redirects)
    |            ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 914, in send
    |     response = self._send_handling_auth(
    |         request,
    |     ...<2 lines>...
    |         history=[],
    |     )
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 942, in _send_handling_auth
    |     response = self._send_handling_redirects(
    |         request,
    |         follow_redirects=follow_redirects,
    |         history=history,
    |     )
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 979, in _send_handling_redirects
    |     response = self._send_single_request(request)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 1014, in _send_single_request
    |     response = transport.handle_request(request)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 348, in handle_request
    |     raise exc
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 345, in handle_request
    |     portal.call(self.app, scope, receive, send)
    |     ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py", line 334, in call
    |     return cast(T_Retval, self.start_task_soon(func, *args).result())
    |                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
    |   File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py", line 450, in result
    |     return self.__get_result()
    |            ~~~~~~~~~~~~~~~~~^^
    |   File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py", line 395, in __get_result
    |     raise self._exception
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py", line 259, in _call_func
    |     retval = await retval_or_awaitable
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py", line 1135, in __call__
    |     await super().__call__(scope, receive, send)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py", line 107, in __call__
    |     await self.middleware_stack(scope, receive, send)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py", line 186, in __call__
    |     raise exc
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py", line 164, in __call__
    |     await self.app(scope, receive, _send)
    |   File "/home/runner/work/stampbot/stampbot/stampbot/main.py", line 205, in __call__
    |     await self.app(scope, receive, send)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py", line 191, in __call__
    |     with recv_stream, send_stream, collapse_excgroups():
    |                                    ~~~~~~~~~~~~~~~~~~^^
    |   File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py", line 162, in __exit__
    |     self.gen.throw(value)
    |     ~~~~~~~~~~~~~~^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py", line 85, in collapse_excgroups
    |     raise exc
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py", line 193, in __call__
    |     response = await self.dispatch_func(request, call_next)
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/work/stampbot/stampbot/stampbot/main.py", line 122, in metrics_middleware
    |     duration = time.time() ** start_time
    |                ~~~~~~~~~~~~^^~~~~~~~~~~~
    | OverflowError: (34, 'Numerical result out of range')
    +------------------------------------

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/runner/work/stampbot/stampbot/tests/test_main.py", line 20, in test_lifespan_startup_shutdown_configured
    response = client.get("/health")
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 473, in get
    return super().get(
           ~~~~~~~~~~~^
        url,
        ^^^^
    ...<6 lines>...
        extensions=extensions,
        ^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 1053, in get
    return self.request(
           ~~~~~~~~~~~~^
        "GET",
        ^^^^^^
    ...<7 lines>...
        extensions=extensions,
        ^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 445, in request
    return super().request(
           ~~~~~~~~~~~~~~~^
        method,
        ^^^^^^^
    ...<11 lines>...
        extensions=extensions,
        ^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 825, in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 914, in send
    response = self._send_handling_auth(
        request,
    ...<2 lines>...
        history=[],
    )
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 942, in _send_handling_auth
    response = self._send_handling_redirects(
        request,
        follow_redirects=follow_redirects,
        history=history,
    )
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 979, in _send_handling_redirects
    response = self._send_single_request(request)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 1014, in _send_single_request
    response = transport.handle_request(request)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 348, in handle_request
    raise exc
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 345, in handle_request
    portal.call(self.app, scope, receive, send)
    ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py", line 334, in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
  File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py", line 450, in result
    return self.__get_result()
           ~~~~~~~~~~~~~~~~~^^
  File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py", line 395, in __get_result
    raise self._exception
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py", line 259, in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py", line 1135, in __call__
    await super().__call__(scope, receive, send)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py", line 107, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py", line 186, in __call__
    raise exc
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py", line 164, in __call__
    await self.app(scope, receive, _send)
  File "/home/runner/work/stampbot/stampbot/stampbot/main.py", line 205, in __call__
    await self.app(scope, receive, send)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py", line 191, in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ~~~~~~~~~~~~~~~~~~^^
  File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py", line 162, in __exit__
    self.gen.throw(value)
    ~~~~~~~~~~~~~~^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py", line 85, in collapse_excgroups
    raise exc
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py", line 193, in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/stampbot/stampbot/stampbot/main.py", line 122, in metrics_middleware
    duration = time.time() ** start_time
               ~~~~~~~~~~~~^^~~~~~~~~~~~
OverflowError: (34, 'Numerical result out of range')

During handling of the above exception, another exception occurred:

    def test_lifespan_startup_shutdown_configured():
        """Test app lifespan startup and shutdown when configured."""
        from stampbot.main import app
    
        # Use TestClient as context manager to trigger lifespan events
        with TestClient(app) as client:
            # App should be running
>           response = client.get("/health")
                       ^^^^^^^^^^^^^^^^^^^^^

tests/test_main.py:20: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:473: in get
    return super().get(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1053: in get
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:450: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

request = <starlette.middleware.base._CachedRequest object at 0x7f2a57286f90>
call_next = <function BaseHTTPMiddleware.__call__.<locals>.call_next at 0x7f2a5728a610>

    @app.middleware("http")
    async def metrics_middleware(request: Request, call_next: Any) -> Response:
        """Middleware to track HTTP metrics.
    
        Args:
            request: Incoming HTTP request.
            call_next: Next middleware or route handler.
    
        Returns:
            HTTP response from downstream handler.
        """
        method = request.method
        endpoint = request.url.path
    
        # Track in-progress requests
        http_requests_in_progress.labels(method=method, endpoint=endpoint).inc()
    
        start_time = time.time()
    
        try:
            response = await call_next(request)
    
>           duration = time.time() ** start_time
                       ^^^^^^^^^^^^^^^^^^^^^^^^^
E           OverflowError: (34, 'Numerical result out of range')

stampbot/main.py:122: OverflowError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:29:49.179270Z [info     ] OpenTelemetry disabled         _logger=<_FixedFindCallerLogger stampbot.telemetry (INFO)> _name=info
2026-03-27T13:29:49.198326Z [info     ] Starting stampbot on 0.0.0.0:8000 _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=info extra={'host': '0.0.0.0', 'port': 8000, 'log_level': 'INFO'}
2026-03-27T13:29:49.198585Z [info     ] GitHub App credentials configured successfully _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=info
2026-03-27T13:29:49.200732Z [info     ] Shutting down stampbot         _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=info
=========================== short test summary info ============================
FAILED tests/test_main.py::test_lifespan_startup_shutdown_configured - OverflowError: (34, 'Numerical result out of range')
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 91 passed, 3 deselected in 1.05s
operator: core/ReplaceBinaryOperator_Sub_Pow, occurrence: 1
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -313,7 +313,7 @@
     try:
         start_time = time.time()
         result = await webhook_handler.handle_event(x_github_event, payload)
-        duration = time.time() - start_time
+        duration = time.time() ** start_time
 
         webhook_processing_duration_seconds.labels(event_type=x_github_event or "unknown").observe(
             duration
........................................................................ [ 34%]
.............................F
=================================== FAILURES ===================================
___________________________ test_webhook_valid_ping ____________________________

test_client = <starlette.testclient.TestClient object at 0x7f76e2fde8b0>

    def test_webhook_valid_ping(test_client: TestClient):
        """Test webhook accepts valid ping event."""
        import hashlib
        import hmac
    
        from stampbot.webhook_handler import webhook_handler
    
        body = json.dumps({"zen": "Design for failure."}).encode()
        signature = (
            "sha256="
            + hmac.new(
                webhook_handler.webhook_secret,
                body,
                hashlib.sha256,
            ).hexdigest()
        )
    
        response = test_client.post(
            "/webhook",
            content=body,
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": signature,
            },
        )
>       assert response.status_code == 200
E       assert 500 == 200
E        +  where 500 = <Response [500 Internal Server Error]>.status_code

tests/test_main.py:175: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:53:16.092397Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-03-27T13:53:16.092601Z [error    ] Error handling webhook event: (34, 'Numerical result out of range') _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=error client_ip=testclient extra={'error': "(34, 'Numerical result out of range')"}
2026-03-27T13:53:16.093387Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 500 Internal Server Error"
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'ping', 'action': ''}, 'event': 'Received ping event', 'client_ip': 'testclient', 'level': 'info', 'timestamp': '2026-03-27T13:53:16.092397Z'}
ERROR    stampbot.main:main.py:326 {'extra': {'error': "(34, 'Numerical result out of range')"}, 'event': "Error handling webhook event: (34, 'Numerical result out of range')", 'client_ip': 'testclient', 'level': 'error', 'timestamp': '2026-03-27T13:53:16.092601Z'}
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 500 Internal Server Error"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_valid_ping - assert 500 == 200
 +  where 500 = <Response [500 Internal Server Error]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 101 passed, 3 deselected in 0.86s
operator: core/ReplaceBinaryOperator_Sub_RShift, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -119,7 +119,7 @@
     try:
         response = await call_next(request)
 
-        duration = time.time() - start_time
+        duration = time.time() >> start_time
 
         # Track request metrics
         http_requests_total.labels(
........................................................................ [ 34%]
...................F
=================================== FAILURES ===================================
__________________ test_lifespan_startup_shutdown_configured ___________________
  + Exception Group Traceback (most recent call last):
  |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py", line 79, in collapse_excgroups
  |     yield
  |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py", line 192, in __call__
  |     async with anyio.create_task_group() as task_group:
  |                ~~~~~~~~~~~~~~~~~~~~~~~^^
  |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/_backends/_asyncio.py", line 783, in __aexit__
  |     raise BaseExceptionGroup(
  |         "unhandled errors in a TaskGroup", self._exceptions
  |     ) from None
  | ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
  +-+---------------- 1 ----------------
    | Traceback (most recent call last):
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/runner.py", line 353, in from_call
    |     result: TResult | None = func()
    |                              ~~~~^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/runner.py", line 245, in <lambda>
    |     lambda: runtest_hook(item=item, **kwds),
    |             ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_hooks.py", line 512, in __call__
    |     return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
    |            ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_manager.py", line 120, in _hookexec
    |     return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
    |            ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 167, in _multicall
    |     raise exception
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 139, in _multicall
    |     teardown.throw(exception)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/logging.py", line 850, in pytest_runtest_call
    |     yield
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 139, in _multicall
    |     teardown.throw(exception)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 53, in run_old_style_hookwrapper
    |     return result.get_result()
    |            ~~~~~~~~~~~~~~~~~^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_result.py", line 103, in get_result
    |     raise exc.with_traceback(tb)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 38, in run_old_style_hookwrapper
    |     res = yield
    |           ^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 139, in _multicall
    |     teardown.throw(exception)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/capture.py", line 900, in pytest_runtest_call
    |     return (yield)
    |             ^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 139, in _multicall
    |     teardown.throw(exception)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/skipping.py", line 268, in pytest_runtest_call
    |     return (yield)
    |             ^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 121, in _multicall
    |     res = hook_impl.function(*args)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/runner.py", line 179, in pytest_runtest_call
    |     item.runtest()
    |     ~~~~~~~~~~~~^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/python.py", line 1720, in runtest
    |     self.ihook.pytest_pyfunc_call(pyfuncitem=self)
    |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_hooks.py", line 512, in __call__
    |     return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
    |            ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_manager.py", line 120, in _hookexec
    |     return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
    |            ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 167, in _multicall
    |     raise exception
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 139, in _multicall
    |     teardown.throw(exception)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 53, in run_old_style_hookwrapper
    |     return result.get_result()
    |            ~~~~~~~~~~~~~~~~~^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_result.py", line 103, in get_result
    |     raise exc.with_traceback(tb)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 38, in run_old_style_hookwrapper
    |     res = yield
    |           ^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 121, in _multicall
    |     res = hook_impl.function(*args)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/python.py", line 166, in pytest_pyfunc_call
    |     result = testfunction(**testargs)
    |   File "/home/runner/work/stampbot/stampbot/tests/test_main.py", line 20, in test_lifespan_startup_shutdown_configured
    |     response = client.get("/health")
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 473, in get
    |     return super().get(
    |            ~~~~~~~~~~~^
    |         url,
    |         ^^^^
    |     ...<6 lines>...
    |         extensions=extensions,
    |         ^^^^^^^^^^^^^^^^^^^^^^
    |     )
    |     ^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 1053, in get
    |     return self.request(
    |            ~~~~~~~~~~~~^
    |         "GET",
    |         ^^^^^^
    |     ...<7 lines>...
    |         extensions=extensions,
    |         ^^^^^^^^^^^^^^^^^^^^^^
    |     )
    |     ^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 445, in request
    |     return super().request(
    |            ~~~~~~~~~~~~~~~^
    |         method,
    |         ^^^^^^^
    |     ...<11 lines>...
    |         extensions=extensions,
    |         ^^^^^^^^^^^^^^^^^^^^^^
    |     )
    |     ^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 825, in request
    |     return self.send(request, auth=auth, follow_redirects=follow_redirects)
    |            ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 914, in send
    |     response = self._send_handling_auth(
    |         request,
    |     ...<2 lines>...
    |         history=[],
    |     )
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 942, in _send_handling_auth
    |     response = self._send_handling_redirects(
    |         request,
    |         follow_redirects=follow_redirects,
    |         history=history,
    |     )
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 979, in _send_handling_redirects
    |     response = self._send_single_request(request)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 1014, in _send_single_request
    |     response = transport.handle_request(request)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 348, in handle_request
    |     raise exc
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 345, in handle_request
    |     portal.call(self.app, scope, receive, send)
    |     ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py", line 334, in call
    |     return cast(T_Retval, self.start_task_soon(func, *args).result())
    |                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
    |   File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py", line 450, in result
    |     return self.__get_result()
    |            ~~~~~~~~~~~~~~~~~^^
    |   File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py", line 395, in __get_result
    |     raise self._exception
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py", line 259, in _call_func
    |     retval = await retval_or_awaitable
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py", line 1135, in __call__
    |     await super().__call__(scope, receive, send)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py", line 107, in __call__
    |     await self.middleware_stack(scope, receive, send)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py", line 186, in __call__
    |     raise exc
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py", line 164, in __call__
    |     await self.app(scope, receive, _send)
    |   File "/home/runner/work/stampbot/stampbot/stampbot/main.py", line 205, in __call__
    |     await self.app(scope, receive, send)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py", line 191, in __call__
    |     with recv_stream, send_stream, collapse_excgroups():
    |                                    ~~~~~~~~~~~~~~~~~~^^
    |   File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py", line 162, in __exit__
    |     self.gen.throw(value)
    |     ~~~~~~~~~~~~~~^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py", line 85, in collapse_excgroups
    |     raise exc
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py", line 193, in __call__
    |     response = await self.dispatch_func(request, call_next)
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/work/stampbot/stampbot/stampbot/main.py", line 122, in metrics_middleware
    |     duration = time.time() >> start_time
    |                ~~~~~~~~~~~~^^~~~~~~~~~~~
    | TypeError: unsupported operand type(s) for >>: 'float' and 'float'
    +------------------------------------

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/runner/work/stampbot/stampbot/tests/test_main.py", line 20, in test_lifespan_startup_shutdown_configured
    response = client.get("/health")
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 473, in get
    return super().get(
           ~~~~~~~~~~~^
        url,
        ^^^^
    ...<6 lines>...
        extensions=extensions,
        ^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 1053, in get
    return self.request(
           ~~~~~~~~~~~~^
        "GET",
        ^^^^^^
    ...<7 lines>...
        extensions=extensions,
        ^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 445, in request
    return super().request(
           ~~~~~~~~~~~~~~~^
        method,
        ^^^^^^^
    ...<11 lines>...
        extensions=extensions,
        ^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 825, in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 914, in send
    response = self._send_handling_auth(
        request,
    ...<2 lines>...
        history=[],
    )
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 942, in _send_handling_auth
    response = self._send_handling_redirects(
        request,
        follow_redirects=follow_redirects,
        history=history,
    )
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 979, in _send_handling_redirects
    response = self._send_single_request(request)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 1014, in _send_single_request
    response = transport.handle_request(request)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 348, in handle_request
    raise exc
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 345, in handle_request
    portal.call(self.app, scope, receive, send)
    ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py", line 334, in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
  File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py", line 450, in result
    return self.__get_result()
           ~~~~~~~~~~~~~~~~~^^
  File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py", line 395, in __get_result
    raise self._exception
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py", line 259, in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py", line 1135, in __call__
    await super().__call__(scope, receive, send)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py", line 107, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py", line 186, in __call__
    raise exc
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py", line 164, in __call__
    await self.app(scope, receive, _send)
  File "/home/runner/work/stampbot/stampbot/stampbot/main.py", line 205, in __call__
    await self.app(scope, receive, send)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py", line 191, in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ~~~~~~~~~~~~~~~~~~^^
  File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py", line 162, in __exit__
    self.gen.throw(value)
    ~~~~~~~~~~~~~~^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py", line 85, in collapse_excgroups
    raise exc
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py", line 193, in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/stampbot/stampbot/stampbot/main.py", line 122, in metrics_middleware
    duration = time.time() >> start_time
               ~~~~~~~~~~~~^^~~~~~~~~~~~
TypeError: unsupported operand type(s) for >>: 'float' and 'float'

During handling of the above exception, another exception occurred:

    def test_lifespan_startup_shutdown_configured():
        """Test app lifespan startup and shutdown when configured."""
        from stampbot.main import app
    
        # Use TestClient as context manager to trigger lifespan events
        with TestClient(app) as client:
            # App should be running
>           response = client.get("/health")
                       ^^^^^^^^^^^^^^^^^^^^^

tests/test_main.py:20: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:473: in get
    return super().get(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1053: in get
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:450: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

request = <starlette.middleware.base._CachedRequest object at 0x7f50cd086f90>
call_next = <function BaseHTTPMiddleware.__call__.<locals>.call_next at 0x7f50cd08a770>

    @app.middleware("http")
    async def metrics_middleware(request: Request, call_next: Any) -> Response:
        """Middleware to track HTTP metrics.
    
        Args:
            request: Incoming HTTP request.
            call_next: Next middleware or route handler.
    
        Returns:
            HTTP response from downstream handler.
        """
        method = request.method
        endpoint = request.url.path
    
        # Track in-progress requests
        http_requests_in_progress.labels(method=method, endpoint=endpoint).inc()
    
        start_time = time.time()
    
        try:
            response = await call_next(request)
    
>           duration = time.time() >> start_time
                       ^^^^^^^^^^^^^^^^^^^^^^^^^
E           TypeError: unsupported operand type(s) for >>: 'float' and 'float'

stampbot/main.py:122: TypeError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:45:59.475377Z [info     ] OpenTelemetry disabled         _logger=<_FixedFindCallerLogger stampbot.telemetry (INFO)> _name=info
2026-03-27T13:45:59.494226Z [info     ] Starting stampbot on 0.0.0.0:8000 _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=info extra={'host': '0.0.0.0', 'port': 8000, 'log_level': 'INFO'}
2026-03-27T13:45:59.494484Z [info     ] GitHub App credentials configured successfully _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=info
2026-03-27T13:45:59.496563Z [info     ] Shutting down stampbot         _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=info
=========================== short test summary info ============================
FAILED tests/test_main.py::test_lifespan_startup_shutdown_configured - TypeError: unsupported operand type(s) for >>: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 91 passed, 3 deselected in 1.04s
operator: core/ReplaceBinaryOperator_Sub_RShift, occurrence: 1
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -313,7 +313,7 @@
     try:
         start_time = time.time()
         result = await webhook_handler.handle_event(x_github_event, payload)
-        duration = time.time() - start_time
+        duration = time.time() >> start_time
 
         webhook_processing_duration_seconds.labels(event_type=x_github_event or "unknown").observe(
             duration
........................................................................ [ 34%]
.............................F
=================================== FAILURES ===================================
___________________________ test_webhook_valid_ping ____________________________

test_client = <starlette.testclient.TestClient object at 0x7fde7ced28b0>

    def test_webhook_valid_ping(test_client: TestClient):
        """Test webhook accepts valid ping event."""
        import hashlib
        import hmac
    
        from stampbot.webhook_handler import webhook_handler
    
        body = json.dumps({"zen": "Design for failure."}).encode()
        signature = (
            "sha256="
            + hmac.new(
                webhook_handler.webhook_secret,
                body,
                hashlib.sha256,
            ).hexdigest()
        )
    
        response = test_client.post(
            "/webhook",
            content=body,
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": signature,
            },
        )
>       assert response.status_code == 200
E       assert 500 == 200
E        +  where 500 = <Response [500 Internal Server Error]>.status_code

tests/test_main.py:175: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:46:22.017875Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-03-27T13:46:22.018079Z [error    ] Error handling webhook event: unsupported operand type(s) for >>: 'float' and 'float' _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=error client_ip=testclient extra={'error': "unsupported operand type(s) for >>: 'float' and 'float'"}
2026-03-27T13:46:22.018853Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 500 Internal Server Error"
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'ping', 'action': ''}, 'event': 'Received ping event', 'client_ip': 'testclient', 'level': 'info', 'timestamp': '2026-03-27T13:46:22.017875Z'}
ERROR    stampbot.main:main.py:326 {'extra': {'error': "unsupported operand type(s) for >>: 'float' and 'float'"}, 'event': "Error handling webhook event: unsupported operand type(s) for >>: 'float' and 'float'", 'client_ip': 'testclient', 'level': 'error', 'timestamp': '2026-03-27T13:46:22.018079Z'}
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 500 Internal Server Error"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_valid_ping - assert 500 == 200
 +  where 500 = <Response [500 Internal Server Error]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 101 passed, 3 deselected in 0.85s
operator: core/ReplaceBinaryOperator_Sub_LShift, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -119,7 +119,7 @@
     try:
         response = await call_next(request)
 
-        duration = time.time() - start_time
+        duration = time.time() << start_time
 
         # Track request metrics
         http_requests_total.labels(
........................................................................ [ 34%]
...................F
=================================== FAILURES ===================================
__________________ test_lifespan_startup_shutdown_configured ___________________
  + Exception Group Traceback (most recent call last):
  |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py", line 79, in collapse_excgroups
  |     yield
  |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py", line 192, in __call__
  |     async with anyio.create_task_group() as task_group:
  |                ~~~~~~~~~~~~~~~~~~~~~~~^^
  |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/_backends/_asyncio.py", line 783, in __aexit__
  |     raise BaseExceptionGroup(
  |         "unhandled errors in a TaskGroup", self._exceptions
  |     ) from None
  | ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
  +-+---------------- 1 ----------------
    | Traceback (most recent call last):
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/runner.py", line 353, in from_call
    |     result: TResult | None = func()
    |                              ~~~~^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/runner.py", line 245, in <lambda>
    |     lambda: runtest_hook(item=item, **kwds),
    |             ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_hooks.py", line 512, in __call__
    |     return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
    |            ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_manager.py", line 120, in _hookexec
    |     return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
    |            ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 167, in _multicall
    |     raise exception
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 139, in _multicall
    |     teardown.throw(exception)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/logging.py", line 850, in pytest_runtest_call
    |     yield
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 139, in _multicall
    |     teardown.throw(exception)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 53, in run_old_style_hookwrapper
    |     return result.get_result()
    |            ~~~~~~~~~~~~~~~~~^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_result.py", line 103, in get_result
    |     raise exc.with_traceback(tb)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 38, in run_old_style_hookwrapper
    |     res = yield
    |           ^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 139, in _multicall
    |     teardown.throw(exception)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/capture.py", line 900, in pytest_runtest_call
    |     return (yield)
    |             ^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 139, in _multicall
    |     teardown.throw(exception)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/skipping.py", line 268, in pytest_runtest_call
    |     return (yield)
    |             ^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 121, in _multicall
    |     res = hook_impl.function(*args)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/runner.py", line 179, in pytest_runtest_call
    |     item.runtest()
    |     ~~~~~~~~~~~~^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/python.py", line 1720, in runtest
    |     self.ihook.pytest_pyfunc_call(pyfuncitem=self)
    |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_hooks.py", line 512, in __call__
    |     return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
    |            ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_manager.py", line 120, in _hookexec
    |     return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
    |            ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 167, in _multicall
    |     raise exception
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 139, in _multicall
    |     teardown.throw(exception)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 53, in run_old_style_hookwrapper
    |     return result.get_result()
    |            ~~~~~~~~~~~~~~~~~^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_result.py", line 103, in get_result
    |     raise exc.with_traceback(tb)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 38, in run_old_style_hookwrapper
    |     res = yield
    |           ^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 121, in _multicall
    |     res = hook_impl.function(*args)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/python.py", line 166, in pytest_pyfunc_call
    |     result = testfunction(**testargs)
    |   File "/home/runner/work/stampbot/stampbot/tests/test_main.py", line 20, in test_lifespan_startup_shutdown_configured
    |     response = client.get("/health")
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 473, in get
    |     return super().get(
    |            ~~~~~~~~~~~^
    |         url,
    |         ^^^^
    |     ...<6 lines>...
    |         extensions=extensions,
    |         ^^^^^^^^^^^^^^^^^^^^^^
    |     )
    |     ^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 1053, in get
    |     return self.request(
    |            ~~~~~~~~~~~~^
    |         "GET",
    |         ^^^^^^
    |     ...<7 lines>...
    |         extensions=extensions,
    |         ^^^^^^^^^^^^^^^^^^^^^^
    |     )
    |     ^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 445, in request
    |     return super().request(
    |            ~~~~~~~~~~~~~~~^
    |         method,
    |         ^^^^^^^
    |     ...<11 lines>...
    |         extensions=extensions,
    |         ^^^^^^^^^^^^^^^^^^^^^^
    |     )
    |     ^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 825, in request
    |     return self.send(request, auth=auth, follow_redirects=follow_redirects)
    |            ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 914, in send
    |     response = self._send_handling_auth(
    |         request,
    |     ...<2 lines>...
    |         history=[],
    |     )
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 942, in _send_handling_auth
    |     response = self._send_handling_redirects(
    |         request,
    |         follow_redirects=follow_redirects,
    |         history=history,
    |     )
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 979, in _send_handling_redirects
    |     response = self._send_single_request(request)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 1014, in _send_single_request
    |     response = transport.handle_request(request)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 348, in handle_request
    |     raise exc
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 345, in handle_request
    |     portal.call(self.app, scope, receive, send)
    |     ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py", line 334, in call
    |     return cast(T_Retval, self.start_task_soon(func, *args).result())
    |                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
    |   File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py", line 450, in result
    |     return self.__get_result()
    |            ~~~~~~~~~~~~~~~~~^^
    |   File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py", line 395, in __get_result
    |     raise self._exception
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py", line 259, in _call_func
    |     retval = await retval_or_awaitable
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py", line 1135, in __call__
    |     await super().__call__(scope, receive, send)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py", line 107, in __call__
    |     await self.middleware_stack(scope, receive, send)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py", line 186, in __call__
    |     raise exc
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py", line 164, in __call__
    |     await self.app(scope, receive, _send)
    |   File "/home/runner/work/stampbot/stampbot/stampbot/main.py", line 205, in __call__
    |     await self.app(scope, receive, send)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py", line 191, in __call__
    |     with recv_stream, send_stream, collapse_excgroups():
    |                                    ~~~~~~~~~~~~~~~~~~^^
    |   File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py", line 162, in __exit__
    |     self.gen.throw(value)
    |     ~~~~~~~~~~~~~~^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py", line 85, in collapse_excgroups
    |     raise exc
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py", line 193, in __call__
    |     response = await self.dispatch_func(request, call_next)
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/work/stampbot/stampbot/stampbot/main.py", line 122, in metrics_middleware
    |     duration = time.time() << start_time
    |                ~~~~~~~~~~~~^^~~~~~~~~~~~
    | TypeError: unsupported operand type(s) for <<: 'float' and 'float'
    +------------------------------------

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/runner/work/stampbot/stampbot/tests/test_main.py", line 20, in test_lifespan_startup_shutdown_configured
    response = client.get("/health")
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 473, in get
    return super().get(
           ~~~~~~~~~~~^
        url,
        ^^^^
    ...<6 lines>...
        extensions=extensions,
        ^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 1053, in get
    return self.request(
           ~~~~~~~~~~~~^
        "GET",
        ^^^^^^
    ...<7 lines>...
        extensions=extensions,
        ^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 445, in request
    return super().request(
           ~~~~~~~~~~~~~~~^
        method,
        ^^^^^^^
    ...<11 lines>...
        extensions=extensions,
        ^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 825, in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 914, in send
    response = self._send_handling_auth(
        request,
    ...<2 lines>...
        history=[],
    )
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 942, in _send_handling_auth
    response = self._send_handling_redirects(
        request,
        follow_redirects=follow_redirects,
        history=history,
    )
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 979, in _send_handling_redirects
    response = self._send_single_request(request)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 1014, in _send_single_request
    response = transport.handle_request(request)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 348, in handle_request
    raise exc
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 345, in handle_request
    portal.call(self.app, scope, receive, send)
    ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py", line 334, in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
  File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py", line 450, in result
    return self.__get_result()
           ~~~~~~~~~~~~~~~~~^^
  File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py", line 395, in __get_result
    raise self._exception
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py", line 259, in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py", line 1135, in __call__
    await super().__call__(scope, receive, send)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py", line 107, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py", line 186, in __call__
    raise exc
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py", line 164, in __call__
    await self.app(scope, receive, _send)
  File "/home/runner/work/stampbot/stampbot/stampbot/main.py", line 205, in __call__
    await self.app(scope, receive, send)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py", line 191, in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ~~~~~~~~~~~~~~~~~~^^
  File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py", line 162, in __exit__
    self.gen.throw(value)
    ~~~~~~~~~~~~~~^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py", line 85, in collapse_excgroups
    raise exc
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py", line 193, in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/stampbot/stampbot/stampbot/main.py", line 122, in metrics_middleware
    duration = time.time() << start_time
               ~~~~~~~~~~~~^^~~~~~~~~~~~
TypeError: unsupported operand type(s) for <<: 'float' and 'float'

During handling of the above exception, another exception occurred:

    def test_lifespan_startup_shutdown_configured():
        """Test app lifespan startup and shutdown when configured."""
        from stampbot.main import app
    
        # Use TestClient as context manager to trigger lifespan events
        with TestClient(app) as client:
            # App should be running
>           response = client.get("/health")
                       ^^^^^^^^^^^^^^^^^^^^^

tests/test_main.py:20: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:473: in get
    return super().get(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1053: in get
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:450: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

request = <starlette.middleware.base._CachedRequest object at 0x7ff58477ef90>
call_next = <function BaseHTTPMiddleware.__call__.<locals>.call_next at 0x7ff584782770>

    @app.middleware("http")
    async def metrics_middleware(request: Request, call_next: Any) -> Response:
        """Middleware to track HTTP metrics.
    
        Args:
            request: Incoming HTTP request.
            call_next: Next middleware or route handler.
    
        Returns:
            HTTP response from downstream handler.
        """
        method = request.method
        endpoint = request.url.path
    
        # Track in-progress requests
        http_requests_in_progress.labels(method=method, endpoint=endpoint).inc()
    
        start_time = time.time()
    
        try:
            response = await call_next(request)
    
>           duration = time.time() << start_time
                       ^^^^^^^^^^^^^^^^^^^^^^^^^
E           TypeError: unsupported operand type(s) for <<: 'float' and 'float'

stampbot/main.py:122: TypeError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:57:15.888502Z [info     ] OpenTelemetry disabled         _logger=<_FixedFindCallerLogger stampbot.telemetry (INFO)> _name=info
2026-03-27T13:57:15.907953Z [info     ] Starting stampbot on 0.0.0.0:8000 _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=info extra={'host': '0.0.0.0', 'port': 8000, 'log_level': 'INFO'}
2026-03-27T13:57:15.908262Z [info     ] GitHub App credentials configured successfully _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=info
2026-03-27T13:57:15.910496Z [info     ] Shutting down stampbot         _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=info
=========================== short test summary info ============================
FAILED tests/test_main.py::test_lifespan_startup_shutdown_configured - TypeError: unsupported operand type(s) for <<: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 91 passed, 3 deselected in 1.07s
operator: core/ReplaceBinaryOperator_Sub_LShift, occurrence: 1
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -313,7 +313,7 @@
     try:
         start_time = time.time()
         result = await webhook_handler.handle_event(x_github_event, payload)
-        duration = time.time() - start_time
+        duration = time.time() << start_time
 
         webhook_processing_duration_seconds.labels(event_type=x_github_event or "unknown").observe(
             duration
........................................................................ [ 34%]
.............................F
=================================== FAILURES ===================================
___________________________ test_webhook_valid_ping ____________________________

test_client = <starlette.testclient.TestClient object at 0x7fec962fa8b0>

    def test_webhook_valid_ping(test_client: TestClient):
        """Test webhook accepts valid ping event."""
        import hashlib
        import hmac
    
        from stampbot.webhook_handler import webhook_handler
    
        body = json.dumps({"zen": "Design for failure."}).encode()
        signature = (
            "sha256="
            + hmac.new(
                webhook_handler.webhook_secret,
                body,
                hashlib.sha256,
            ).hexdigest()
        )
    
        response = test_client.post(
            "/webhook",
            content=body,
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": signature,
            },
        )
>       assert response.status_code == 200
E       assert 500 == 200
E        +  where 500 = <Response [500 Internal Server Error]>.status_code

tests/test_main.py:175: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:49:04.266267Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-03-27T13:49:04.266466Z [error    ] Error handling webhook event: unsupported operand type(s) for <<: 'float' and 'float' _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=error client_ip=testclient extra={'error': "unsupported operand type(s) for <<: 'float' and 'float'"}
2026-03-27T13:49:04.267277Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 500 Internal Server Error"
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'ping', 'action': ''}, 'event': 'Received ping event', 'client_ip': 'testclient', 'level': 'info', 'timestamp': '2026-03-27T13:49:04.266267Z'}
ERROR    stampbot.main:main.py:326 {'extra': {'error': "unsupported operand type(s) for <<: 'float' and 'float'"}, 'event': "Error handling webhook event: unsupported operand type(s) for <<: 'float' and 'float'", 'client_ip': 'testclient', 'level': 'error', 'timestamp': '2026-03-27T13:49:04.266466Z'}
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 500 Internal Server Error"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_valid_ping - assert 500 == 200
 +  where 500 = <Response [500 Internal Server Error]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 101 passed, 3 deselected in 0.85s
operator: core/ReplaceBinaryOperator_Sub_BitOr, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -119,7 +119,7 @@
     try:
         response = await call_next(request)
 
-        duration = time.time() - start_time
+        duration = time.time() | start_time
 
         # Track request metrics
         http_requests_total.labels(
........................................................................ [ 34%]
...................F
=================================== FAILURES ===================================
__________________ test_lifespan_startup_shutdown_configured ___________________
  + Exception Group Traceback (most recent call last):
  |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py", line 79, in collapse_excgroups
  |     yield
  |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py", line 192, in __call__
  |     async with anyio.create_task_group() as task_group:
  |                ~~~~~~~~~~~~~~~~~~~~~~~^^
  |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/_backends/_asyncio.py", line 783, in __aexit__
  |     raise BaseExceptionGroup(
  |         "unhandled errors in a TaskGroup", self._exceptions
  |     ) from None
  | ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
  +-+---------------- 1 ----------------
    | Traceback (most recent call last):
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/runner.py", line 353, in from_call
    |     result: TResult | None = func()
    |                              ~~~~^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/runner.py", line 245, in <lambda>
    |     lambda: runtest_hook(item=item, **kwds),
    |             ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_hooks.py", line 512, in __call__
    |     return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
    |            ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_manager.py", line 120, in _hookexec
    |     return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
    |            ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 167, in _multicall
    |     raise exception
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 139, in _multicall
    |     teardown.throw(exception)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/logging.py", line 850, in pytest_runtest_call
    |     yield
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 139, in _multicall
    |     teardown.throw(exception)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 53, in run_old_style_hookwrapper
    |     return result.get_result()
    |            ~~~~~~~~~~~~~~~~~^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_result.py", line 103, in get_result
    |     raise exc.with_traceback(tb)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 38, in run_old_style_hookwrapper
    |     res = yield
    |           ^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 139, in _multicall
    |     teardown.throw(exception)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/capture.py", line 900, in pytest_runtest_call
    |     return (yield)
    |             ^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 139, in _multicall
    |     teardown.throw(exception)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/skipping.py", line 268, in pytest_runtest_call
    |     return (yield)
    |             ^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 121, in _multicall
    |     res = hook_impl.function(*args)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/runner.py", line 179, in pytest_runtest_call
    |     item.runtest()
    |     ~~~~~~~~~~~~^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/python.py", line 1720, in runtest
    |     self.ihook.pytest_pyfunc_call(pyfuncitem=self)
    |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_hooks.py", line 512, in __call__
    |     return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
    |            ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_manager.py", line 120, in _hookexec
    |     return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
    |            ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 167, in _multicall
    |     raise exception
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 139, in _multicall
    |     teardown.throw(exception)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 53, in run_old_style_hookwrapper
    |     return result.get_result()
    |            ~~~~~~~~~~~~~~~~~^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_result.py", line 103, in get_result
    |     raise exc.with_traceback(tb)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 38, in run_old_style_hookwrapper
    |     res = yield
    |           ^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 121, in _multicall
    |     res = hook_impl.function(*args)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/python.py", line 166, in pytest_pyfunc_call
    |     result = testfunction(**testargs)
    |   File "/home/runner/work/stampbot/stampbot/tests/test_main.py", line 20, in test_lifespan_startup_shutdown_configured
    |     response = client.get("/health")
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 473, in get
    |     return super().get(
    |            ~~~~~~~~~~~^
    |         url,
    |         ^^^^
    |     ...<6 lines>...
    |         extensions=extensions,
    |         ^^^^^^^^^^^^^^^^^^^^^^
    |     )
    |     ^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 1053, in get
    |     return self.request(
    |            ~~~~~~~~~~~~^
    |         "GET",
    |         ^^^^^^
    |     ...<7 lines>...
    |         extensions=extensions,
    |         ^^^^^^^^^^^^^^^^^^^^^^
    |     )
    |     ^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 445, in request
    |     return super().request(
    |            ~~~~~~~~~~~~~~~^
    |         method,
    |         ^^^^^^^
    |     ...<11 lines>...
    |         extensions=extensions,
    |         ^^^^^^^^^^^^^^^^^^^^^^
    |     )
    |     ^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 825, in request
    |     return self.send(request, auth=auth, follow_redirects=follow_redirects)
    |            ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 914, in send
    |     response = self._send_handling_auth(
    |         request,
    |     ...<2 lines>...
    |         history=[],
    |     )
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 942, in _send_handling_auth
    |     response = self._send_handling_redirects(
    |         request,
    |         follow_redirects=follow_redirects,
    |         history=history,
    |     )
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 979, in _send_handling_redirects
    |     response = self._send_single_request(request)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 1014, in _send_single_request
    |     response = transport.handle_request(request)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 348, in handle_request
    |     raise exc
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 345, in handle_request
    |     portal.call(self.app, scope, receive, send)
    |     ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py", line 334, in call
    |     return cast(T_Retval, self.start_task_soon(func, *args).result())
    |                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
    |   File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py", line 450, in result
    |     return self.__get_result()
    |            ~~~~~~~~~~~~~~~~~^^
    |   File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py", line 395, in __get_result
    |     raise self._exception
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py", line 259, in _call_func
    |     retval = await retval_or_awaitable
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py", line 1135, in __call__
    |     await super().__call__(scope, receive, send)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py", line 107, in __call__
    |     await self.middleware_stack(scope, receive, send)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py", line 186, in __call__
    |     raise exc
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py", line 164, in __call__
    |     await self.app(scope, receive, _send)
    |   File "/home/runner/work/stampbot/stampbot/stampbot/main.py", line 205, in __call__
    |     await self.app(scope, receive, send)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py", line 191, in __call__
    |     with recv_stream, send_stream, collapse_excgroups():
    |                                    ~~~~~~~~~~~~~~~~~~^^
    |   File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py", line 162, in __exit__
    |     self.gen.throw(value)
    |     ~~~~~~~~~~~~~~^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py", line 85, in collapse_excgroups
    |     raise exc
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py", line 193, in __call__
    |     response = await self.dispatch_func(request, call_next)
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/work/stampbot/stampbot/stampbot/main.py", line 122, in metrics_middleware
    |     duration = time.time() | start_time
    |                ~~~~~~~~~~~~^~~~~~~~~~~~
    | TypeError: unsupported operand type(s) for |: 'float' and 'float'
    +------------------------------------

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/runner/work/stampbot/stampbot/tests/test_main.py", line 20, in test_lifespan_startup_shutdown_configured
    response = client.get("/health")
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 473, in get
    return super().get(
           ~~~~~~~~~~~^
        url,
        ^^^^
    ...<6 lines>...
        extensions=extensions,
        ^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 1053, in get
    return self.request(
           ~~~~~~~~~~~~^
        "GET",
        ^^^^^^
    ...<7 lines>...
        extensions=extensions,
        ^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 445, in request
    return super().request(
           ~~~~~~~~~~~~~~~^
        method,
        ^^^^^^^
    ...<11 lines>...
        extensions=extensions,
        ^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 825, in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 914, in send
    response = self._send_handling_auth(
        request,
    ...<2 lines>...
        history=[],
    )
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 942, in _send_handling_auth
    response = self._send_handling_redirects(
        request,
        follow_redirects=follow_redirects,
        history=history,
    )
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 979, in _send_handling_redirects
    response = self._send_single_request(request)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 1014, in _send_single_request
    response = transport.handle_request(request)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 348, in handle_request
    raise exc
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 345, in handle_request
    portal.call(self.app, scope, receive, send)
    ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py", line 334, in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
  File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py", line 450, in result
    return self.__get_result()
           ~~~~~~~~~~~~~~~~~^^
  File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py", line 395, in __get_result
    raise self._exception
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py", line 259, in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py", line 1135, in __call__
    await super().__call__(scope, receive, send)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py", line 107, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py", line 186, in __call__
    raise exc
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py", line 164, in __call__
    await self.app(scope, receive, _send)
  File "/home/runner/work/stampbot/stampbot/stampbot/main.py", line 205, in __call__
    await self.app(scope, receive, send)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py", line 191, in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ~~~~~~~~~~~~~~~~~~^^
  File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py", line 162, in __exit__
    self.gen.throw(value)
    ~~~~~~~~~~~~~~^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py", line 85, in collapse_excgroups
    raise exc
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py", line 193, in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/stampbot/stampbot/stampbot/main.py", line 122, in metrics_middleware
    duration = time.time() | start_time
               ~~~~~~~~~~~~^~~~~~~~~~~~
TypeError: unsupported operand type(s) for |: 'float' and 'float'

During handling of the above exception, another exception occurred:

    def test_lifespan_startup_shutdown_configured():
        """Test app lifespan startup and shutdown when configured."""
        from stampbot.main import app
    
        # Use TestClient as context manager to trigger lifespan events
        with TestClient(app) as client:
            # App should be running
>           response = client.get("/health")
                       ^^^^^^^^^^^^^^^^^^^^^

tests/test_main.py:20: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:473: in get
    return super().get(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1053: in get
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:450: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

request = <starlette.middleware.base._CachedRequest object at 0x7fab50d86f90>
call_next = <function BaseHTTPMiddleware.__call__.<locals>.call_next at 0x7fab50d8a770>

    @app.middleware("http")
    async def metrics_middleware(request: Request, call_next: Any) -> Response:
        """Middleware to track HTTP metrics.
    
        Args:
            request: Incoming HTTP request.
            call_next: Next middleware or route handler.
    
        Returns:
            HTTP response from downstream handler.
        """
        method = request.method
        endpoint = request.url.path
    
        # Track in-progress requests
        http_requests_in_progress.labels(method=method, endpoint=endpoint).inc()
    
        start_time = time.time()
    
        try:
            response = await call_next(request)
    
>           duration = time.time() | start_time
                       ^^^^^^^^^^^^^^^^^^^^^^^^
E           TypeError: unsupported operand type(s) for |: 'float' and 'float'

stampbot/main.py:122: TypeError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:00:53.970483Z [info     ] OpenTelemetry disabled         _logger=<_FixedFindCallerLogger stampbot.telemetry (INFO)> _name=info
2026-03-27T14:00:53.989272Z [info     ] Starting stampbot on 0.0.0.0:8000 _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=info extra={'host': '0.0.0.0', 'port': 8000, 'log_level': 'INFO'}
2026-03-27T14:00:53.989522Z [info     ] GitHub App credentials configured successfully _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=info
2026-03-27T14:00:53.991591Z [info     ] Shutting down stampbot         _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=info
=========================== short test summary info ============================
FAILED tests/test_main.py::test_lifespan_startup_shutdown_configured - TypeError: unsupported operand type(s) for |: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 91 passed, 3 deselected in 1.05s
operator: core/ReplaceBinaryOperator_Sub_BitOr, occurrence: 1
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -313,7 +313,7 @@
     try:
         start_time = time.time()
         result = await webhook_handler.handle_event(x_github_event, payload)
-        duration = time.time() - start_time
+        duration = time.time() | start_time
 
         webhook_processing_duration_seconds.labels(event_type=x_github_event or "unknown").observe(
             duration
........................................................................ [ 34%]
.............................F
=================================== FAILURES ===================================
___________________________ test_webhook_valid_ping ____________________________

test_client = <starlette.testclient.TestClient object at 0x7f8bbced28b0>

    def test_webhook_valid_ping(test_client: TestClient):
        """Test webhook accepts valid ping event."""
        import hashlib
        import hmac
    
        from stampbot.webhook_handler import webhook_handler
    
        body = json.dumps({"zen": "Design for failure."}).encode()
        signature = (
            "sha256="
            + hmac.new(
                webhook_handler.webhook_secret,
                body,
                hashlib.sha256,
            ).hexdigest()
        )
    
        response = test_client.post(
            "/webhook",
            content=body,
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": signature,
            },
        )
>       assert response.status_code == 200
E       assert 500 == 200
E        +  where 500 = <Response [500 Internal Server Error]>.status_code

tests/test_main.py:175: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:07:02.724276Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-03-27T14:07:02.724479Z [error    ] Error handling webhook event: unsupported operand type(s) for |: 'float' and 'float' _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=error client_ip=testclient extra={'error': "unsupported operand type(s) for |: 'float' and 'float'"}
2026-03-27T14:07:02.725257Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 500 Internal Server Error"
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'ping', 'action': ''}, 'event': 'Received ping event', 'client_ip': 'testclient', 'level': 'info', 'timestamp': '2026-03-27T14:07:02.724276Z'}
ERROR    stampbot.main:main.py:326 {'extra': {'error': "unsupported operand type(s) for |: 'float' and 'float'"}, 'event': "Error handling webhook event: unsupported operand type(s) for |: 'float' and 'float'", 'client_ip': 'testclient', 'level': 'error', 'timestamp': '2026-03-27T14:07:02.724479Z'}
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 500 Internal Server Error"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_valid_ping - assert 500 == 200
 +  where 500 = <Response [500 Internal Server Error]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 101 passed, 3 deselected in 0.86s
operator: core/ReplaceBinaryOperator_Sub_BitAnd, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -119,7 +119,7 @@
     try:
         response = await call_next(request)
 
-        duration = time.time() - start_time
+        duration = time.time() & start_time
 
         # Track request metrics
         http_requests_total.labels(
........................................................................ [ 34%]
...................F
=================================== FAILURES ===================================
__________________ test_lifespan_startup_shutdown_configured ___________________
  + Exception Group Traceback (most recent call last):
  |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py", line 79, in collapse_excgroups
  |     yield
  |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py", line 192, in __call__
  |     async with anyio.create_task_group() as task_group:
  |                ~~~~~~~~~~~~~~~~~~~~~~~^^
  |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/_backends/_asyncio.py", line 783, in __aexit__
  |     raise BaseExceptionGroup(
  |         "unhandled errors in a TaskGroup", self._exceptions
  |     ) from None
  | ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
  +-+---------------- 1 ----------------
    | Traceback (most recent call last):
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/runner.py", line 353, in from_call
    |     result: TResult | None = func()
    |                              ~~~~^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/runner.py", line 245, in <lambda>
    |     lambda: runtest_hook(item=item, **kwds),
    |             ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_hooks.py", line 512, in __call__
    |     return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
    |            ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_manager.py", line 120, in _hookexec
    |     return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
    |            ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 167, in _multicall
    |     raise exception
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 139, in _multicall
    |     teardown.throw(exception)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/logging.py", line 850, in pytest_runtest_call
    |     yield
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 139, in _multicall
    |     teardown.throw(exception)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 53, in run_old_style_hookwrapper
    |     return result.get_result()
    |            ~~~~~~~~~~~~~~~~~^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_result.py", line 103, in get_result
    |     raise exc.with_traceback(tb)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 38, in run_old_style_hookwrapper
    |     res = yield
    |           ^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 139, in _multicall
    |     teardown.throw(exception)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/capture.py", line 900, in pytest_runtest_call
    |     return (yield)
    |             ^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 139, in _multicall
    |     teardown.throw(exception)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/skipping.py", line 268, in pytest_runtest_call
    |     return (yield)
    |             ^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 121, in _multicall
    |     res = hook_impl.function(*args)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/runner.py", line 179, in pytest_runtest_call
    |     item.runtest()
    |     ~~~~~~~~~~~~^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/python.py", line 1720, in runtest
    |     self.ihook.pytest_pyfunc_call(pyfuncitem=self)
    |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_hooks.py", line 512, in __call__
    |     return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
    |            ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_manager.py", line 120, in _hookexec
    |     return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
    |            ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 167, in _multicall
    |     raise exception
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 139, in _multicall
    |     teardown.throw(exception)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 53, in run_old_style_hookwrapper
    |     return result.get_result()
    |            ~~~~~~~~~~~~~~~~~^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_result.py", line 103, in get_result
    |     raise exc.with_traceback(tb)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 38, in run_old_style_hookwrapper
    |     res = yield
    |           ^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 121, in _multicall
    |     res = hook_impl.function(*args)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/python.py", line 166, in pytest_pyfunc_call
    |     result = testfunction(**testargs)
    |   File "/home/runner/work/stampbot/stampbot/tests/test_main.py", line 20, in test_lifespan_startup_shutdown_configured
    |     response = client.get("/health")
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 473, in get
    |     return super().get(
    |            ~~~~~~~~~~~^
    |         url,
    |         ^^^^
    |     ...<6 lines>...
    |         extensions=extensions,
    |         ^^^^^^^^^^^^^^^^^^^^^^
    |     )
    |     ^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 1053, in get
    |     return self.request(
    |            ~~~~~~~~~~~~^
    |         "GET",
    |         ^^^^^^
    |     ...<7 lines>...
    |         extensions=extensions,
    |         ^^^^^^^^^^^^^^^^^^^^^^
    |     )
    |     ^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 445, in request
    |     return super().request(
    |            ~~~~~~~~~~~~~~~^
    |         method,
    |         ^^^^^^^
    |     ...<11 lines>...
    |         extensions=extensions,
    |         ^^^^^^^^^^^^^^^^^^^^^^
    |     )
    |     ^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 825, in request
    |     return self.send(request, auth=auth, follow_redirects=follow_redirects)
    |            ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 914, in send
    |     response = self._send_handling_auth(
    |         request,
    |     ...<2 lines>...
    |         history=[],
    |     )
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 942, in _send_handling_auth
    |     response = self._send_handling_redirects(
    |         request,
    |         follow_redirects=follow_redirects,
    |         history=history,
    |     )
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 979, in _send_handling_redirects
    |     response = self._send_single_request(request)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 1014, in _send_single_request
    |     response = transport.handle_request(request)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 348, in handle_request
    |     raise exc
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 345, in handle_request
    |     portal.call(self.app, scope, receive, send)
    |     ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py", line 334, in call
    |     return cast(T_Retval, self.start_task_soon(func, *args).result())
    |                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
    |   File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py", line 450, in result
    |     return self.__get_result()
    |            ~~~~~~~~~~~~~~~~~^^
    |   File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py", line 395, in __get_result
    |     raise self._exception
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py", line 259, in _call_func
    |     retval = await retval_or_awaitable
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py", line 1135, in __call__
    |     await super().__call__(scope, receive, send)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py", line 107, in __call__
    |     await self.middleware_stack(scope, receive, send)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py", line 186, in __call__
    |     raise exc
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py", line 164, in __call__
    |     await self.app(scope, receive, _send)
    |   File "/home/runner/work/stampbot/stampbot/stampbot/main.py", line 205, in __call__
    |     await self.app(scope, receive, send)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py", line 191, in __call__
    |     with recv_stream, send_stream, collapse_excgroups():
    |                                    ~~~~~~~~~~~~~~~~~~^^
    |   File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py", line 162, in __exit__
    |     self.gen.throw(value)
    |     ~~~~~~~~~~~~~~^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py", line 85, in collapse_excgroups
    |     raise exc
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py", line 193, in __call__
    |     response = await self.dispatch_func(request, call_next)
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/work/stampbot/stampbot/stampbot/main.py", line 122, in metrics_middleware
    |     duration = time.time() & start_time
    |                ~~~~~~~~~~~~^~~~~~~~~~~~
    | TypeError: unsupported operand type(s) for &: 'float' and 'float'
    +------------------------------------

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/runner/work/stampbot/stampbot/tests/test_main.py", line 20, in test_lifespan_startup_shutdown_configured
    response = client.get("/health")
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 473, in get
    return super().get(
           ~~~~~~~~~~~^
        url,
        ^^^^
    ...<6 lines>...
        extensions=extensions,
        ^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 1053, in get
    return self.request(
           ~~~~~~~~~~~~^
        "GET",
        ^^^^^^
    ...<7 lines>...
        extensions=extensions,
        ^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 445, in request
    return super().request(
           ~~~~~~~~~~~~~~~^
        method,
        ^^^^^^^
    ...<11 lines>...
        extensions=extensions,
        ^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 825, in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 914, in send
    response = self._send_handling_auth(
        request,
    ...<2 lines>...
        history=[],
    )
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 942, in _send_handling_auth
    response = self._send_handling_redirects(
        request,
        follow_redirects=follow_redirects,
        history=history,
    )
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 979, in _send_handling_redirects
    response = self._send_single_request(request)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 1014, in _send_single_request
    response = transport.handle_request(request)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 348, in handle_request
    raise exc
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 345, in handle_request
    portal.call(self.app, scope, receive, send)
    ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py", line 334, in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
  File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py", line 450, in result
    return self.__get_result()
           ~~~~~~~~~~~~~~~~~^^
  File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py", line 395, in __get_result
    raise self._exception
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py", line 259, in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py", line 1135, in __call__
    await super().__call__(scope, receive, send)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py", line 107, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py", line 186, in __call__
    raise exc
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py", line 164, in __call__
    await self.app(scope, receive, _send)
  File "/home/runner/work/stampbot/stampbot/stampbot/main.py", line 205, in __call__
    await self.app(scope, receive, send)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py", line 191, in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ~~~~~~~~~~~~~~~~~~^^
  File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py", line 162, in __exit__
    self.gen.throw(value)
    ~~~~~~~~~~~~~~^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py", line 85, in collapse_excgroups
    raise exc
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py", line 193, in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/stampbot/stampbot/stampbot/main.py", line 122, in metrics_middleware
    duration = time.time() & start_time
               ~~~~~~~~~~~~^~~~~~~~~~~~
TypeError: unsupported operand type(s) for &: 'float' and 'float'

During handling of the above exception, another exception occurred:

    def test_lifespan_startup_shutdown_configured():
        """Test app lifespan startup and shutdown when configured."""
        from stampbot.main import app
    
        # Use TestClient as context manager to trigger lifespan events
        with TestClient(app) as client:
            # App should be running
>           response = client.get("/health")
                       ^^^^^^^^^^^^^^^^^^^^^

tests/test_main.py:20: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:473: in get
    return super().get(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1053: in get
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:450: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

request = <starlette.middleware.base._CachedRequest object at 0x7fc843caaf90>
call_next = <function BaseHTTPMiddleware.__call__.<locals>.call_next at 0x7fc843cae770>

    @app.middleware("http")
    async def metrics_middleware(request: Request, call_next: Any) -> Response:
        """Middleware to track HTTP metrics.
    
        Args:
            request: Incoming HTTP request.
            call_next: Next middleware or route handler.
    
        Returns:
            HTTP response from downstream handler.
        """
        method = request.method
        endpoint = request.url.path
    
        # Track in-progress requests
        http_requests_in_progress.labels(method=method, endpoint=endpoint).inc()
    
        start_time = time.time()
    
        try:
            response = await call_next(request)
    
>           duration = time.time() & start_time
                       ^^^^^^^^^^^^^^^^^^^^^^^^
E           TypeError: unsupported operand type(s) for &: 'float' and 'float'

stampbot/main.py:122: TypeError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:54:26.276085Z [info     ] OpenTelemetry disabled         _logger=<_FixedFindCallerLogger stampbot.telemetry (INFO)> _name=info
2026-03-27T13:54:26.295556Z [info     ] Starting stampbot on 0.0.0.0:8000 _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=info extra={'host': '0.0.0.0', 'port': 8000, 'log_level': 'INFO'}
2026-03-27T13:54:26.295867Z [info     ] GitHub App credentials configured successfully _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=info
2026-03-27T13:54:26.298011Z [info     ] Shutting down stampbot         _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=info
=========================== short test summary info ============================
FAILED tests/test_main.py::test_lifespan_startup_shutdown_configured - TypeError: unsupported operand type(s) for &: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 91 passed, 3 deselected in 1.05s
operator: core/ReplaceBinaryOperator_Sub_BitAnd, occurrence: 1
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -313,7 +313,7 @@
     try:
         start_time = time.time()
         result = await webhook_handler.handle_event(x_github_event, payload)
-        duration = time.time() - start_time
+        duration = time.time() & start_time
 
         webhook_processing_duration_seconds.labels(event_type=x_github_event or "unknown").observe(
             duration
........................................................................ [ 34%]
.............................F
=================================== FAILURES ===================================
___________________________ test_webhook_valid_ping ____________________________

test_client = <starlette.testclient.TestClient object at 0x7f6da53de8b0>

    def test_webhook_valid_ping(test_client: TestClient):
        """Test webhook accepts valid ping event."""
        import hashlib
        import hmac
    
        from stampbot.webhook_handler import webhook_handler
    
        body = json.dumps({"zen": "Design for failure."}).encode()
        signature = (
            "sha256="
            + hmac.new(
                webhook_handler.webhook_secret,
                body,
                hashlib.sha256,
            ).hexdigest()
        )
    
        response = test_client.post(
            "/webhook",
            content=body,
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": signature,
            },
        )
>       assert response.status_code == 200
E       assert 500 == 200
E        +  where 500 = <Response [500 Internal Server Error]>.status_code

tests/test_main.py:175: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:33:13.236906Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-03-27T13:33:13.237112Z [error    ] Error handling webhook event: unsupported operand type(s) for &: 'float' and 'float' _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=error client_ip=testclient extra={'error': "unsupported operand type(s) for &: 'float' and 'float'"}
2026-03-27T13:33:13.237924Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 500 Internal Server Error"
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'ping', 'action': ''}, 'event': 'Received ping event', 'client_ip': 'testclient', 'level': 'info', 'timestamp': '2026-03-27T13:33:13.236906Z'}
ERROR    stampbot.main:main.py:326 {'extra': {'error': "unsupported operand type(s) for &: 'float' and 'float'"}, 'event': "Error handling webhook event: unsupported operand type(s) for &: 'float' and 'float'", 'client_ip': 'testclient', 'level': 'error', 'timestamp': '2026-03-27T13:33:13.237112Z'}
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 500 Internal Server Error"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_valid_ping - assert 500 == 200
 +  where 500 = <Response [500 Internal Server Error]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 101 passed, 3 deselected in 0.84s
operator: core/ReplaceBinaryOperator_Sub_BitXor, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -119,7 +119,7 @@
     try:
         response = await call_next(request)
 
-        duration = time.time() - start_time
+        duration = time.time() ^ start_time
 
         # Track request metrics
         http_requests_total.labels(
........................................................................ [ 34%]
...................F
=================================== FAILURES ===================================
__________________ test_lifespan_startup_shutdown_configured ___________________
  + Exception Group Traceback (most recent call last):
  |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py", line 79, in collapse_excgroups
  |     yield
  |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py", line 192, in __call__
  |     async with anyio.create_task_group() as task_group:
  |                ~~~~~~~~~~~~~~~~~~~~~~~^^
  |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/_backends/_asyncio.py", line 783, in __aexit__
  |     raise BaseExceptionGroup(
  |         "unhandled errors in a TaskGroup", self._exceptions
  |     ) from None
  | ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
  +-+---------------- 1 ----------------
    | Traceback (most recent call last):
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/runner.py", line 353, in from_call
    |     result: TResult | None = func()
    |                              ~~~~^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/runner.py", line 245, in <lambda>
    |     lambda: runtest_hook(item=item, **kwds),
    |             ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_hooks.py", line 512, in __call__
    |     return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
    |            ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_manager.py", line 120, in _hookexec
    |     return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
    |            ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 167, in _multicall
    |     raise exception
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 139, in _multicall
    |     teardown.throw(exception)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/logging.py", line 850, in pytest_runtest_call
    |     yield
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 139, in _multicall
    |     teardown.throw(exception)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 53, in run_old_style_hookwrapper
    |     return result.get_result()
    |            ~~~~~~~~~~~~~~~~~^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_result.py", line 103, in get_result
    |     raise exc.with_traceback(tb)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 38, in run_old_style_hookwrapper
    |     res = yield
    |           ^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 139, in _multicall
    |     teardown.throw(exception)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/capture.py", line 900, in pytest_runtest_call
    |     return (yield)
    |             ^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 139, in _multicall
    |     teardown.throw(exception)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/skipping.py", line 268, in pytest_runtest_call
    |     return (yield)
    |             ^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 121, in _multicall
    |     res = hook_impl.function(*args)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/runner.py", line 179, in pytest_runtest_call
    |     item.runtest()
    |     ~~~~~~~~~~~~^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/python.py", line 1720, in runtest
    |     self.ihook.pytest_pyfunc_call(pyfuncitem=self)
    |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_hooks.py", line 512, in __call__
    |     return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
    |            ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_manager.py", line 120, in _hookexec
    |     return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
    |            ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 167, in _multicall
    |     raise exception
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 139, in _multicall
    |     teardown.throw(exception)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 53, in run_old_style_hookwrapper
    |     return result.get_result()
    |            ~~~~~~~~~~~~~~~~~^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_result.py", line 103, in get_result
    |     raise exc.with_traceback(tb)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 38, in run_old_style_hookwrapper
    |     res = yield
    |           ^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 121, in _multicall
    |     res = hook_impl.function(*args)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/python.py", line 166, in pytest_pyfunc_call
    |     result = testfunction(**testargs)
    |   File "/home/runner/work/stampbot/stampbot/tests/test_main.py", line 20, in test_lifespan_startup_shutdown_configured
    |     response = client.get("/health")
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 473, in get
    |     return super().get(
    |            ~~~~~~~~~~~^
    |         url,
    |         ^^^^
    |     ...<6 lines>...
    |         extensions=extensions,
    |         ^^^^^^^^^^^^^^^^^^^^^^
    |     )
    |     ^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 1053, in get
    |     return self.request(
    |            ~~~~~~~~~~~~^
    |         "GET",
    |         ^^^^^^
    |     ...<7 lines>...
    |         extensions=extensions,
    |         ^^^^^^^^^^^^^^^^^^^^^^
    |     )
    |     ^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 445, in request
    |     return super().request(
    |            ~~~~~~~~~~~~~~~^
    |         method,
    |         ^^^^^^^
    |     ...<11 lines>...
    |         extensions=extensions,
    |         ^^^^^^^^^^^^^^^^^^^^^^
    |     )
    |     ^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 825, in request
    |     return self.send(request, auth=auth, follow_redirects=follow_redirects)
    |            ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 914, in send
    |     response = self._send_handling_auth(
    |         request,
    |     ...<2 lines>...
    |         history=[],
    |     )
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 942, in _send_handling_auth
    |     response = self._send_handling_redirects(
    |         request,
    |         follow_redirects=follow_redirects,
    |         history=history,
    |     )
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 979, in _send_handling_redirects
    |     response = self._send_single_request(request)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 1014, in _send_single_request
    |     response = transport.handle_request(request)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 348, in handle_request
    |     raise exc
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 345, in handle_request
    |     portal.call(self.app, scope, receive, send)
    |     ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py", line 334, in call
    |     return cast(T_Retval, self.start_task_soon(func, *args).result())
    |                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
    |   File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py", line 450, in result
    |     return self.__get_result()
    |            ~~~~~~~~~~~~~~~~~^^
    |   File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py", line 395, in __get_result
    |     raise self._exception
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py", line 259, in _call_func
    |     retval = await retval_or_awaitable
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py", line 1135, in __call__
    |     await super().__call__(scope, receive, send)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py", line 107, in __call__
    |     await self.middleware_stack(scope, receive, send)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py", line 186, in __call__
    |     raise exc
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py", line 164, in __call__
    |     await self.app(scope, receive, _send)
    |   File "/home/runner/work/stampbot/stampbot/stampbot/main.py", line 205, in __call__
    |     await self.app(scope, receive, send)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py", line 191, in __call__
    |     with recv_stream, send_stream, collapse_excgroups():
    |                                    ~~~~~~~~~~~~~~~~~~^^
    |   File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py", line 162, in __exit__
    |     self.gen.throw(value)
    |     ~~~~~~~~~~~~~~^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py", line 85, in collapse_excgroups
    |     raise exc
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py", line 193, in __call__
    |     response = await self.dispatch_func(request, call_next)
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/work/stampbot/stampbot/stampbot/main.py", line 122, in metrics_middleware
    |     duration = time.time() ^ start_time
    |                ~~~~~~~~~~~~^~~~~~~~~~~~
    | TypeError: unsupported operand type(s) for ^: 'float' and 'float'
    +------------------------------------

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/runner/work/stampbot/stampbot/tests/test_main.py", line 20, in test_lifespan_startup_shutdown_configured
    response = client.get("/health")
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 473, in get
    return super().get(
           ~~~~~~~~~~~^
        url,
        ^^^^
    ...<6 lines>...
        extensions=extensions,
        ^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 1053, in get
    return self.request(
           ~~~~~~~~~~~~^
        "GET",
        ^^^^^^
    ...<7 lines>...
        extensions=extensions,
        ^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 445, in request
    return super().request(
           ~~~~~~~~~~~~~~~^
        method,
        ^^^^^^^
    ...<11 lines>...
        extensions=extensions,
        ^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 825, in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 914, in send
    response = self._send_handling_auth(
        request,
    ...<2 lines>...
        history=[],
    )
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 942, in _send_handling_auth
    response = self._send_handling_redirects(
        request,
        follow_redirects=follow_redirects,
        history=history,
    )
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 979, in _send_handling_redirects
    response = self._send_single_request(request)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 1014, in _send_single_request
    response = transport.handle_request(request)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 348, in handle_request
    raise exc
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 345, in handle_request
    portal.call(self.app, scope, receive, send)
    ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py", line 334, in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
  File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py", line 450, in result
    return self.__get_result()
           ~~~~~~~~~~~~~~~~~^^
  File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py", line 395, in __get_result
    raise self._exception
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py", line 259, in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py", line 1135, in __call__
    await super().__call__(scope, receive, send)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py", line 107, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py", line 186, in __call__
    raise exc
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py", line 164, in __call__
    await self.app(scope, receive, _send)
  File "/home/runner/work/stampbot/stampbot/stampbot/main.py", line 205, in __call__
    await self.app(scope, receive, send)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py", line 191, in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ~~~~~~~~~~~~~~~~~~^^
  File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py", line 162, in __exit__
    self.gen.throw(value)
    ~~~~~~~~~~~~~~^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py", line 85, in collapse_excgroups
    raise exc
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py", line 193, in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/stampbot/stampbot/stampbot/main.py", line 122, in metrics_middleware
    duration = time.time() ^ start_time
               ~~~~~~~~~~~~^~~~~~~~~~~~
TypeError: unsupported operand type(s) for ^: 'float' and 'float'

During handling of the above exception, another exception occurred:

    def test_lifespan_startup_shutdown_configured():
        """Test app lifespan startup and shutdown when configured."""
        from stampbot.main import app
    
        # Use TestClient as context manager to trigger lifespan events
        with TestClient(app) as client:
            # App should be running
>           response = client.get("/health")
                       ^^^^^^^^^^^^^^^^^^^^^

tests/test_main.py:20: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:473: in get
    return super().get(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1053: in get
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:450: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

request = <starlette.middleware.base._CachedRequest object at 0x7ff8a7872f90>
call_next = <function BaseHTTPMiddleware.__call__.<locals>.call_next at 0x7ff8a7876770>

    @app.middleware("http")
    async def metrics_middleware(request: Request, call_next: Any) -> Response:
        """Middleware to track HTTP metrics.
    
        Args:
            request: Incoming HTTP request.
            call_next: Next middleware or route handler.
    
        Returns:
            HTTP response from downstream handler.
        """
        method = request.method
        endpoint = request.url.path
    
        # Track in-progress requests
        http_requests_in_progress.labels(method=method, endpoint=endpoint).inc()
    
        start_time = time.time()
    
        try:
            response = await call_next(request)
    
>           duration = time.time() ^ start_time
                       ^^^^^^^^^^^^^^^^^^^^^^^^
E           TypeError: unsupported operand type(s) for ^: 'float' and 'float'

stampbot/main.py:122: TypeError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:52:26.215339Z [info     ] OpenTelemetry disabled         _logger=<_FixedFindCallerLogger stampbot.telemetry (INFO)> _name=info
2026-03-27T13:52:26.234083Z [info     ] Starting stampbot on 0.0.0.0:8000 _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=info extra={'host': '0.0.0.0', 'port': 8000, 'log_level': 'INFO'}
2026-03-27T13:52:26.234373Z [info     ] GitHub App credentials configured successfully _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=info
2026-03-27T13:52:26.236545Z [info     ] Shutting down stampbot         _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=info
=========================== short test summary info ============================
FAILED tests/test_main.py::test_lifespan_startup_shutdown_configured - TypeError: unsupported operand type(s) for ^: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 91 passed, 3 deselected in 1.03s
operator: core/ReplaceBinaryOperator_Sub_BitXor, occurrence: 1
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -313,7 +313,7 @@
     try:
         start_time = time.time()
         result = await webhook_handler.handle_event(x_github_event, payload)
-        duration = time.time() - start_time
+        duration = time.time() ^ start_time
 
         webhook_processing_duration_seconds.labels(event_type=x_github_event or "unknown").observe(
             duration
........................................................................ [ 34%]
.............................F
=================================== FAILURES ===================================
___________________________ test_webhook_valid_ping ____________________________

test_client = <starlette.testclient.TestClient object at 0x7fbeca2ea8b0>

    def test_webhook_valid_ping(test_client: TestClient):
        """Test webhook accepts valid ping event."""
        import hashlib
        import hmac
    
        from stampbot.webhook_handler import webhook_handler
    
        body = json.dumps({"zen": "Design for failure."}).encode()
        signature = (
            "sha256="
            + hmac.new(
                webhook_handler.webhook_secret,
                body,
                hashlib.sha256,
            ).hexdigest()
        )
    
        response = test_client.post(
            "/webhook",
            content=body,
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": signature,
            },
        )
>       assert response.status_code == 200
E       assert 500 == 200
E        +  where 500 = <Response [500 Internal Server Error]>.status_code

tests/test_main.py:175: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:43:24.857427Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-03-27T13:43:24.857622Z [error    ] Error handling webhook event: unsupported operand type(s) for ^: 'float' and 'float' _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=error client_ip=testclient extra={'error': "unsupported operand type(s) for ^: 'float' and 'float'"}
2026-03-27T13:43:24.858432Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 500 Internal Server Error"
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'ping', 'action': ''}, 'event': 'Received ping event', 'client_ip': 'testclient', 'level': 'info', 'timestamp': '2026-03-27T13:43:24.857427Z'}
ERROR    stampbot.main:main.py:326 {'extra': {'error': "unsupported operand type(s) for ^: 'float' and 'float'"}, 'event': "Error handling webhook event: unsupported operand type(s) for ^: 'float' and 'float'", 'client_ip': 'testclient', 'level': 'error', 'timestamp': '2026-03-27T13:43:24.857622Z'}
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 500 Internal Server Error"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_valid_ping - assert 500 == 200
 +  where 500 = <Response [500 Internal Server Error]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 101 passed, 3 deselected in 0.85s
operator: core/ReplaceBinaryOperator_Mul_Add, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -47,7 +47,7 @@
 APP_VERSION = "0.1.0"
 
 # Security limits
-MAX_WEBHOOK_BODY_SIZE = 1024 * 1024  # 1MB - GitHub webhooks are typically much smaller
+MAX_WEBHOOK_BODY_SIZE = 1024 + 1024  # 1MB - GitHub webhooks are typically much smaller
 
 
 @asynccontextmanager
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Mul_Sub, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -47,7 +47,7 @@
 APP_VERSION = "0.1.0"
 
 # Security limits
-MAX_WEBHOOK_BODY_SIZE = 1024 * 1024  # 1MB - GitHub webhooks are typically much smaller
+MAX_WEBHOOK_BODY_SIZE = 1024 - 1024  # 1MB - GitHub webhooks are typically much smaller
 
 
 @asynccontextmanager
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7f1bfe3b3770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
        response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )
        # Will fail signature validation, but Content-Length tracking happens before that
>       assert response.status_code == 401
E       assert 413 == 401
E        +  where 413 = <Response [413 Request Entity Too Large]>.status_code

tests/test_main.py:79: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:02:19.839549Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 413 Request Entity Too Large"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 413 Request Entity Too Large"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - assert 413 == 401
 +  where 413 = <Response [413 Request Entity Too Large]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 0.84s
operator: core/ReplaceBinaryOperator_Mul_Div, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -47,7 +47,7 @@
 APP_VERSION = "0.1.0"
 
 # Security limits
-MAX_WEBHOOK_BODY_SIZE = 1024 * 1024  # 1MB - GitHub webhooks are typically much smaller
+MAX_WEBHOOK_BODY_SIZE = 1024 / 1024  # 1MB - GitHub webhooks are typically much smaller
 
 
 @asynccontextmanager
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7f9378f83770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
        response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )
        # Will fail signature validation, but Content-Length tracking happens before that
>       assert response.status_code == 401
E       assert 413 == 401
E        +  where 413 = <Response [413 Request Entity Too Large]>.status_code

tests/test_main.py:79: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:57:37.871176Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 413 Request Entity Too Large"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 413 Request Entity Too Large"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - assert 413 == 401
 +  where 413 = <Response [413 Request Entity Too Large]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 0.84s
operator: core/ReplaceBinaryOperator_Mul_FloorDiv, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -47,7 +47,7 @@
 APP_VERSION = "0.1.0"
 
 # Security limits
-MAX_WEBHOOK_BODY_SIZE = 1024 * 1024  # 1MB - GitHub webhooks are typically much smaller
+MAX_WEBHOOK_BODY_SIZE = 1024 // 1024  # 1MB - GitHub webhooks are typically much smaller
 
 
 @asynccontextmanager
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7fba01197770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
        response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )
        # Will fail signature validation, but Content-Length tracking happens before that
>       assert response.status_code == 401
E       assert 413 == 401
E        +  where 413 = <Response [413 Request Entity Too Large]>.status_code

tests/test_main.py:79: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:40:02.502953Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 413 Request Entity Too Large"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 413 Request Entity Too Large"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - assert 413 == 401
 +  where 413 = <Response [413 Request Entity Too Large]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 0.83s
operator: core/ReplaceBinaryOperator_Mul_Mod, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -47,7 +47,7 @@
 APP_VERSION = "0.1.0"
 
 # Security limits
-MAX_WEBHOOK_BODY_SIZE = 1024 * 1024  # 1MB - GitHub webhooks are typically much smaller
+MAX_WEBHOOK_BODY_SIZE = 1024 % 1024  # 1MB - GitHub webhooks are typically much smaller
 
 
 @asynccontextmanager
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7f44b7fa7770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
        response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )
        # Will fail signature validation, but Content-Length tracking happens before that
>       assert response.status_code == 401
E       assert 413 == 401
E        +  where 413 = <Response [413 Request Entity Too Large]>.status_code

tests/test_main.py:79: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:52:12.799279Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 413 Request Entity Too Large"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 413 Request Entity Too Large"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - assert 413 == 401
 +  where 413 = <Response [413 Request Entity Too Large]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 0.84s
operator: core/ReplaceBinaryOperator_Mul_Pow, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -47,7 +47,7 @@
 APP_VERSION = "0.1.0"
 
 # Security limits
-MAX_WEBHOOK_BODY_SIZE = 1024 * 1024  # 1MB - GitHub webhooks are typically much smaller
+MAX_WEBHOOK_BODY_SIZE = 1024 ** 1024  # 1MB - GitHub webhooks are typically much smaller
 
 
 @asynccontextmanager
........................................................................ [ 34%]
..............................F
=================================== FAILURES ===================================
____________________ test_webhook_content_length_too_large _____________________

test_client = <starlette.testclient.TestClient object at 0x7f4957dcaf10>

    def test_webhook_content_length_too_large(test_client: TestClient):
        """Test webhook rejects requests with Content-Length exceeding limit."""
        response = test_client.post(
            "/webhook",
            content=b"x",  # Small actual body
            headers={
                "Content-Type": "application/json",
                "Content-Length": "20000000",  # 20MB - exceeds 10MB limit
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=fake",
            },
        )
>       assert response.status_code == 413
E       assert 401 == 413
E        +  where 401 = <Response [401 Unauthorized]>.status_code

tests/test_main.py:193: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:52:20.669701Z [warning  ] Invalid webhook signature      _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=warning client_ip=testclient
2026-03-27T13:52:20.670492Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 401 Unauthorized"
------------------------------ Captured log call -------------------------------
WARNING  stampbot.main:main.py:299 {'event': 'Invalid webhook signature', 'client_ip': 'testclient', 'level': 'warning', 'timestamp': '2026-03-27T13:52:20.669701Z'}
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 401 Unauthorized"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_content_length_too_large - assert 401 == 413
 +  where 401 = <Response [401 Unauthorized]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 102 passed, 3 deselected in 0.85s
operator: core/ReplaceBinaryOperator_Mul_RShift, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -47,7 +47,7 @@
 APP_VERSION = "0.1.0"
 
 # Security limits
-MAX_WEBHOOK_BODY_SIZE = 1024 * 1024  # 1MB - GitHub webhooks are typically much smaller
+MAX_WEBHOOK_BODY_SIZE = 1024 >> 1024  # 1MB - GitHub webhooks are typically much smaller
 
 
 @asynccontextmanager
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7f34fcba7770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
        response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )
        # Will fail signature validation, but Content-Length tracking happens before that
>       assert response.status_code == 401
E       assert 413 == 401
E        +  where 413 = <Response [413 Request Entity Too Large]>.status_code

tests/test_main.py:79: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:45:41.545458Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 413 Request Entity Too Large"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 413 Request Entity Too Large"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - assert 413 == 401
 +  where 413 = <Response [413 Request Entity Too Large]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 0.83s
operator: core/ReplaceBinaryOperator_Mul_LShift, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -47,7 +47,7 @@
 APP_VERSION = "0.1.0"
 
 # Security limits
-MAX_WEBHOOK_BODY_SIZE = 1024 * 1024  # 1MB - GitHub webhooks are typically much smaller
+MAX_WEBHOOK_BODY_SIZE = 1024 << 1024  # 1MB - GitHub webhooks are typically much smaller
 
 
 @asynccontextmanager
........................................................................ [ 34%]
..............................F
=================================== FAILURES ===================================
____________________ test_webhook_content_length_too_large _____________________

test_client = <starlette.testclient.TestClient object at 0x7f025d2def10>

    def test_webhook_content_length_too_large(test_client: TestClient):
        """Test webhook rejects requests with Content-Length exceeding limit."""
        response = test_client.post(
            "/webhook",
            content=b"x",  # Small actual body
            headers={
                "Content-Type": "application/json",
                "Content-Length": "20000000",  # 20MB - exceeds 10MB limit
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=fake",
            },
        )
>       assert response.status_code == 413
E       assert 401 == 413
E        +  where 401 = <Response [401 Unauthorized]>.status_code

tests/test_main.py:193: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:03:31.181636Z [warning  ] Invalid webhook signature      _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=warning client_ip=testclient
2026-03-27T14:03:31.182423Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 401 Unauthorized"
------------------------------ Captured log call -------------------------------
WARNING  stampbot.main:main.py:299 {'event': 'Invalid webhook signature', 'client_ip': 'testclient', 'level': 'warning', 'timestamp': '2026-03-27T14:03:31.181636Z'}
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 401 Unauthorized"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_content_length_too_large - assert 401 == 413
 +  where 401 = <Response [401 Unauthorized]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 102 passed, 3 deselected in 0.86s
operator: core/ReplaceBinaryOperator_Mul_BitOr, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -47,7 +47,7 @@
 APP_VERSION = "0.1.0"
 
 # Security limits
-MAX_WEBHOOK_BODY_SIZE = 1024 * 1024  # 1MB - GitHub webhooks are typically much smaller
+MAX_WEBHOOK_BODY_SIZE = 1024 | 1024  # 1MB - GitHub webhooks are typically much smaller
 
 
 @asynccontextmanager
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Mul_BitAnd, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -47,7 +47,7 @@
 APP_VERSION = "0.1.0"
 
 # Security limits
-MAX_WEBHOOK_BODY_SIZE = 1024 * 1024  # 1MB - GitHub webhooks are typically much smaller
+MAX_WEBHOOK_BODY_SIZE = 1024 & 1024  # 1MB - GitHub webhooks are typically much smaller
 
 
 @asynccontextmanager
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Mul_BitXor, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -47,7 +47,7 @@
 APP_VERSION = "0.1.0"
 
 # Security limits
-MAX_WEBHOOK_BODY_SIZE = 1024 * 1024  # 1MB - GitHub webhooks are typically much smaller
+MAX_WEBHOOK_BODY_SIZE = 1024 ^ 1024  # 1MB - GitHub webhooks are typically much smaller
 
 
 @asynccontextmanager
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7f84109ab770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
        response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )
        # Will fail signature validation, but Content-Length tracking happens before that
>       assert response.status_code == 401
E       assert 413 == 401
E        +  where 413 = <Response [413 Request Entity Too Large]>.status_code

tests/test_main.py:79: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:55:31.248609Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 413 Request Entity Too Large"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 413 Request Entity Too Large"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - assert 413 == 401
 +  where 413 = <Response [413 Request Entity Too Large]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 0.84s
operator: core/ReplaceComparisonOperator_Eq_NotEq, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -183,7 +183,7 @@
             receive: ASGI receive callable.
             send: ASGI send callable.
         """
-        if scope["type"] == "http":
+        if scope["type"] != "http":
             structlog.contextvars.clear_contextvars()
 
             client_ip: str | None = None
........................................................................ [ 34%]
...................F
=================================== FAILURES ===================================
__________________ test_lifespan_startup_shutdown_configured ___________________

    def test_lifespan_startup_shutdown_configured():
        """Test app lifespan startup and shutdown when configured."""
        from stampbot.main import app
    
        # Use TestClient as context manager to trigger lifespan events
>       with TestClient(app) as client:
             ^^^^^^^^^^^^^^^

tests/test_main.py:18: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:688: in __enter__
    portal.call(self.wait_startup)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:450: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:717: in wait_startup
    message = await receive()
              ^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:714: in receive
    self.task.result()
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:704: in lifespan
    await self.app(scope, self.stream_receive.receive, self.stream_send.send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:151: in __call__
    await self.app(scope, receive, send)
stampbot/main.py:193: in __call__
    raw = Request(scope).headers.get(client_ip_header)
          ^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/requests.py:204: in __init__
    super().__init__(scope)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <starlette.requests.Request object at 0x7f5d3d5567b0>
scope = {'app': <fastapi.applications.FastAPI object at 0x7f5d3d78af90>, 'state': {}, 'type': 'lifespan'}
receive = None

    def __init__(self, scope: Scope, receive: Receive | None = None) -> None:
>       assert scope["type"] in ("http", "websocket")
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E       AssertionError

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/requests.py:78: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:54:51.311597Z [info     ] OpenTelemetry disabled         _logger=<_FixedFindCallerLogger stampbot.telemetry (INFO)> _name=info
=========================== short test summary info ============================
FAILED tests/test_main.py::test_lifespan_startup_shutdown_configured - AssertionError
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 91 passed, 3 deselected in 0.96s
operator: core/ReplaceComparisonOperator_Eq_Lt, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -183,7 +183,7 @@
             receive: ASGI receive callable.
             send: ASGI send callable.
         """
-        if scope["type"] == "http":
+        if scope["type"] < "http":
             structlog.contextvars.clear_contextvars()
 
             client_ip: str | None = None
........................................................................ [ 34%]
...................................F
=================================== FAILURES ===================================
______________ test_logging_middleware_respects_configured_header ______________

    def test_logging_middleware_respects_configured_header():
        """Test that logging_middleware uses the configured client_ip_header setting."""
        from unittest.mock import patch
    
        import structlog.contextvars
    
        from stampbot.main import app
    
        with patch("stampbot.main.settings") as mock_settings:
            mock_settings.get = lambda key, default=None: (
                "X-Real-IP" if key == "client_ip_header" else default
            )
    
            client = TestClient(app)
            captured: dict[str, str | None] = {}
    
            original_bind = structlog.contextvars.bind_contextvars
    
            def capturing_bind(**kw: object) -> None:
                captured.update({k: str(v) for k, v in kw.items()})
                original_bind(**kw)
    
            with patch("stampbot.main.structlog.contextvars.bind_contextvars", capturing_bind):
                client.get("/health", headers={"X-Real-IP": "198.51.100.7"})
    
>           assert captured.get("client_ip") == "198.51.100.7"
E           AssertionError: assert None == '198.51.100.7'
E            +  where None = <built-in method get of dict object at 0x7f828fd07880>('client_ip')
E            +    where <built-in method get of dict object at 0x7f828fd07880> = {}.get

tests/test_main.py:296: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:44:37.170450Z [info     ] HTTP Request: GET http://testserver/health "HTTP/1.1 200 OK"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: GET http://testserver/health "HTTP/1.1 200 OK"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_logging_middleware_respects_configured_header - AssertionError: assert None == '198.51.100.7'
 +  where None = <built-in method get of dict object at 0x7f828fd07880>('client_ip')
 +    where <built-in method get of dict object at 0x7f828fd07880> = {}.get
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 107 passed, 3 deselected in 0.87s
operator: core/ReplaceComparisonOperator_Eq_LtE, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -183,7 +183,7 @@
             receive: ASGI receive callable.
             send: ASGI send callable.
         """
-        if scope["type"] == "http":
+        if scope["type"] <= "http":
             structlog.contextvars.clear_contextvars()
 
             client_ip: str | None = None
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceComparisonOperator_Eq_Gt, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -183,7 +183,7 @@
             receive: ASGI receive callable.
             send: ASGI send callable.
         """
-        if scope["type"] == "http":
+        if scope["type"] > "http":
             structlog.contextvars.clear_contextvars()
 
             client_ip: str | None = None
........................................................................ [ 34%]
...................F
=================================== FAILURES ===================================
__________________ test_lifespan_startup_shutdown_configured ___________________

    def test_lifespan_startup_shutdown_configured():
        """Test app lifespan startup and shutdown when configured."""
        from stampbot.main import app
    
        # Use TestClient as context manager to trigger lifespan events
>       with TestClient(app) as client:
             ^^^^^^^^^^^^^^^

tests/test_main.py:18: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:688: in __enter__
    portal.call(self.wait_startup)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:450: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:717: in wait_startup
    message = await receive()
              ^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:714: in receive
    self.task.result()
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:704: in lifespan
    await self.app(scope, self.stream_receive.receive, self.stream_send.send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:151: in __call__
    await self.app(scope, receive, send)
stampbot/main.py:193: in __call__
    raw = Request(scope).headers.get(client_ip_header)
          ^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/requests.py:204: in __init__
    super().__init__(scope)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <starlette.requests.Request object at 0x7fdb579867b0>
scope = {'app': <fastapi.applications.FastAPI object at 0x7fdb57bbef90>, 'state': {}, 'type': 'lifespan'}
receive = None

    def __init__(self, scope: Scope, receive: Receive | None = None) -> None:
>       assert scope["type"] in ("http", "websocket")
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E       AssertionError

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/requests.py:78: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:39:51.443960Z [info     ] OpenTelemetry disabled         _logger=<_FixedFindCallerLogger stampbot.telemetry (INFO)> _name=info
=========================== short test summary info ============================
FAILED tests/test_main.py::test_lifespan_startup_shutdown_configured - AssertionError
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 91 passed, 3 deselected in 0.97s
operator: core/ReplaceComparisonOperator_Eq_GtE, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -183,7 +183,7 @@
             receive: ASGI receive callable.
             send: ASGI send callable.
         """
-        if scope["type"] == "http":
+        if scope["type"] >= "http":
             structlog.contextvars.clear_contextvars()
 
             client_ip: str | None = None
........................................................................ [ 34%]
...................F
=================================== FAILURES ===================================
__________________ test_lifespan_startup_shutdown_configured ___________________

    def test_lifespan_startup_shutdown_configured():
        """Test app lifespan startup and shutdown when configured."""
        from stampbot.main import app
    
        # Use TestClient as context manager to trigger lifespan events
>       with TestClient(app) as client:
             ^^^^^^^^^^^^^^^

tests/test_main.py:18: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:688: in __enter__
    portal.call(self.wait_startup)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:450: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:717: in wait_startup
    message = await receive()
              ^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:714: in receive
    self.task.result()
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:704: in lifespan
    await self.app(scope, self.stream_receive.receive, self.stream_send.send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:151: in __call__
    await self.app(scope, receive, send)
stampbot/main.py:193: in __call__
    raw = Request(scope).headers.get(client_ip_header)
          ^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/requests.py:204: in __init__
    super().__init__(scope)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <starlette.requests.Request object at 0x7fa0de1867b0>
scope = {'app': <fastapi.applications.FastAPI object at 0x7fa0de3baf90>, 'state': {}, 'type': 'lifespan'}
receive = None

    def __init__(self, scope: Scope, receive: Receive | None = None) -> None:
>       assert scope["type"] in ("http", "websocket")
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E       AssertionError

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/requests.py:78: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:54:31.062039Z [info     ] OpenTelemetry disabled         _logger=<_FixedFindCallerLogger stampbot.telemetry (INFO)> _name=info
=========================== short test summary info ============================
FAILED tests/test_main.py::test_lifespan_startup_shutdown_configured - AssertionError
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 91 passed, 3 deselected in 0.96s
operator: core/ReplaceComparisonOperator_Eq_Is, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -183,7 +183,7 @@
             receive: ASGI receive callable.
             send: ASGI send callable.
         """
-        if scope["type"] == "http":
+        if scope["type"] is "http":
             structlog.contextvars.clear_contextvars()
 
             client_ip: str | None = None
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
=============================== warnings summary ===============================
tests/test_main.py::test_lifespan_startup_shutdown_configured
  /home/runner/work/stampbot/stampbot/stampbot/main.py:186: SyntaxWarning: "is" with 'str' literal. Did you mean "=="?
    if scope["type"] is "http":

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
211 passed, 3 deselected, 1 warning in 1.61s
operator: core/ReplaceComparisonOperator_Eq_IsNot, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -183,7 +183,7 @@
             receive: ASGI receive callable.
             send: ASGI send callable.
         """
-        if scope["type"] == "http":
+        if scope["type"] is not "http":
             structlog.contextvars.clear_contextvars()
 
             client_ip: str | None = None
........................................................................ [ 34%]
...................F
=================================== FAILURES ===================================
__________________ test_lifespan_startup_shutdown_configured ___________________

    def test_lifespan_startup_shutdown_configured():
        """Test app lifespan startup and shutdown when configured."""
        from stampbot.main import app
    
        # Use TestClient as context manager to trigger lifespan events
>       with TestClient(app) as client:
             ^^^^^^^^^^^^^^^

tests/test_main.py:18: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:688: in __enter__
    portal.call(self.wait_startup)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:717: in wait_startup
    message = await receive()
              ^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:714: in receive
    self.task.result()
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:704: in lifespan
    await self.app(scope, self.stream_receive.receive, self.stream_send.send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:151: in __call__
    await self.app(scope, receive, send)
stampbot/main.py:193: in __call__
    raw = Request(scope).headers.get(client_ip_header)
          ^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/requests.py:204: in __init__
    super().__init__(scope)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <starlette.requests.Request object at 0x7f6bca97a900>
scope = {'app': <fastapi.applications.FastAPI object at 0x7f6bcabaf0e0>, 'state': {}, 'type': 'lifespan'}
receive = None

    def __init__(self, scope: Scope, receive: Receive | None = None) -> None:
>       assert scope["type"] in ("http", "websocket")
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E       AssertionError

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/requests.py:78: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:26:44.349228Z [info     ] OpenTelemetry disabled         _logger=<_FixedFindCallerLogger stampbot.telemetry (INFO)> _name=info
=============================== warnings summary ===============================
tests/test_main.py::test_lifespan_startup_shutdown_configured
  /home/runner/work/stampbot/stampbot/stampbot/main.py:186: SyntaxWarning: "is not" with 'str' literal. Did you mean "!="?
    if scope["type"] is not "http":

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=========================== short test summary info ============================
FAILED tests/test_main.py::test_lifespan_startup_shutdown_configured - AssertionError
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 91 passed, 3 deselected, 1 warning in 0.96s
operator: core/ReplaceComparisonOperator_Gt_Eq, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -280,7 +280,7 @@
 
     # Check content length before reading body to prevent memory exhaustion
     content_length = request.headers.get("content-length")
-    if content_length and int(content_length) > MAX_WEBHOOK_BODY_SIZE:
+    if content_length and int(content_length) == MAX_WEBHOOK_BODY_SIZE:
         errors_total.labels(error_type="payload_too_large").inc()
         raise HTTPException(status_code=413, detail="Request body too large")
 
........................................................................ [ 34%]
..............................F
=================================== FAILURES ===================================
____________________ test_webhook_content_length_too_large _____________________

test_client = <starlette.testclient.TestClient object at 0x7f46fedd2f10>

    def test_webhook_content_length_too_large(test_client: TestClient):
        """Test webhook rejects requests with Content-Length exceeding limit."""
        response = test_client.post(
            "/webhook",
            content=b"x",  # Small actual body
            headers={
                "Content-Type": "application/json",
                "Content-Length": "20000000",  # 20MB - exceeds 10MB limit
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=fake",
            },
        )
>       assert response.status_code == 413
E       assert 401 == 413
E        +  where 401 = <Response [401 Unauthorized]>.status_code

tests/test_main.py:193: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:00:43.892543Z [warning  ] Invalid webhook signature      _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=warning client_ip=testclient
2026-03-27T14:00:43.893401Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 401 Unauthorized"
------------------------------ Captured log call -------------------------------
WARNING  stampbot.main:main.py:299 {'event': 'Invalid webhook signature', 'client_ip': 'testclient', 'level': 'warning', 'timestamp': '2026-03-27T14:00:43.892543Z'}
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 401 Unauthorized"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_content_length_too_large - assert 401 == 413
 +  where 401 = <Response [401 Unauthorized]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 102 passed, 3 deselected in 0.86s
operator: core/ReplaceComparisonOperator_Gt_Eq, occurrence: 1
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -288,7 +288,7 @@
     body = await request.body()
 
     # Double-check actual body size (in case content-length was missing/incorrect)
-    if len(body) > MAX_WEBHOOK_BODY_SIZE:
+    if len(body) == MAX_WEBHOOK_BODY_SIZE:
         errors_total.labels(error_type="payload_too_large").inc()
         raise HTTPException(status_code=413, detail="Request body too large")
 
........................................................................ [ 34%]
...............................F
=================================== FAILURES ===================================
_________________________ test_webhook_body_too_large __________________________

test_client = <starlette.testclient.TestClient object at 0x7f23c0fab790>

    def test_webhook_body_too_large(test_client: TestClient):
        """Test webhook rejects requests where actual body exceeds limit.
    
        This tests the secondary body size check (lines 231-232) which catches
        cases where Content-Length header is missing or incorrect.
        """
        # Create a body larger than MAX_WEBHOOK_BODY_SIZE (10MB)
        large_body = b"x" * (10 * 1024 * 1024 + 1)  # 10MB + 1 byte
    
        # Set Content-Length to a small value to bypass the header check (line 222)
        # but the actual body size check (line 230) should still catch it
        response = test_client.post(
            "/webhook",
            content=large_body,
            headers={
                "Content-Type": "application/json",
                "Content-Length": "100",  # Lie about size to bypass first check
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=fake",
            },
        )
>       assert response.status_code == 413
E       assert 401 == 413
E        +  where 401 = <Response [401 Unauthorized]>.status_code

tests/test_main.py:218: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:28:52.673315Z [warning  ] Invalid webhook signature      _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=warning client_ip=testclient
2026-03-27T13:28:52.674336Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 401 Unauthorized"
------------------------------ Captured log call -------------------------------
WARNING  stampbot.main:main.py:299 {'event': 'Invalid webhook signature', 'client_ip': 'testclient', 'level': 'warning', 'timestamp': '2026-03-27T13:28:52.673315Z'}
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 401 Unauthorized"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_body_too_large - assert 401 == 413
 +  where 401 = <Response [401 Unauthorized]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 103 passed, 3 deselected in 0.87s
operator: core/ReplaceComparisonOperator_Gt_NotEq, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -280,7 +280,7 @@
 
     # Check content length before reading body to prevent memory exhaustion
     content_length = request.headers.get("content-length")
-    if content_length and int(content_length) > MAX_WEBHOOK_BODY_SIZE:
+    if content_length and int(content_length) != MAX_WEBHOOK_BODY_SIZE:
         errors_total.labels(error_type="payload_too_large").inc()
         raise HTTPException(status_code=413, detail="Request body too large")
 
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7fd230ba3770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
        response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )
        # Will fail signature validation, but Content-Length tracking happens before that
>       assert response.status_code == 401
E       assert 413 == 401
E        +  where 413 = <Response [413 Request Entity Too Large]>.status_code

tests/test_main.py:79: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:36:01.242072Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 413 Request Entity Too Large"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 413 Request Entity Too Large"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - assert 413 == 401
 +  where 413 = <Response [413 Request Entity Too Large]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 0.83s
operator: core/ReplaceComparisonOperator_Gt_NotEq, occurrence: 1
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -288,7 +288,7 @@
     body = await request.body()
 
     # Double-check actual body size (in case content-length was missing/incorrect)
-    if len(body) > MAX_WEBHOOK_BODY_SIZE:
+    if len(body) != MAX_WEBHOOK_BODY_SIZE:
         errors_total.labels(error_type="payload_too_large").inc()
         raise HTTPException(status_code=413, detail="Request body too large")
 
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7f8576bab770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
        response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )
        # Will fail signature validation, but Content-Length tracking happens before that
>       assert response.status_code == 401
E       assert 413 == 401
E        +  where 413 = <Response [413 Request Entity Too Large]>.status_code

tests/test_main.py:79: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:06:13.115649Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 413 Request Entity Too Large"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 413 Request Entity Too Large"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - assert 413 == 401
 +  where 413 = <Response [413 Request Entity Too Large]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 0.85s
operator: core/ReplaceComparisonOperator_Gt_Lt, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -280,7 +280,7 @@
 
     # Check content length before reading body to prevent memory exhaustion
     content_length = request.headers.get("content-length")
-    if content_length and int(content_length) > MAX_WEBHOOK_BODY_SIZE:
+    if content_length and int(content_length) < MAX_WEBHOOK_BODY_SIZE:
         errors_total.labels(error_type="payload_too_large").inc()
         raise HTTPException(status_code=413, detail="Request body too large")
 
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7fda57db3770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
        response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )
        # Will fail signature validation, but Content-Length tracking happens before that
>       assert response.status_code == 401
E       assert 413 == 401
E        +  where 413 = <Response [413 Request Entity Too Large]>.status_code

tests/test_main.py:79: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:06:24.018801Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 413 Request Entity Too Large"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 413 Request Entity Too Large"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - assert 413 == 401
 +  where 413 = <Response [413 Request Entity Too Large]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 0.84s
operator: core/ReplaceComparisonOperator_Gt_Lt, occurrence: 1
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -288,7 +288,7 @@
     body = await request.body()
 
     # Double-check actual body size (in case content-length was missing/incorrect)
-    if len(body) > MAX_WEBHOOK_BODY_SIZE:
+    if len(body) < MAX_WEBHOOK_BODY_SIZE:
         errors_total.labels(error_type="payload_too_large").inc()
         raise HTTPException(status_code=413, detail="Request body too large")
 
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7f21f29af770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
        response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )
        # Will fail signature validation, but Content-Length tracking happens before that
>       assert response.status_code == 401
E       assert 413 == 401
E        +  where 413 = <Response [413 Request Entity Too Large]>.status_code

tests/test_main.py:79: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:03:04.244839Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 413 Request Entity Too Large"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 413 Request Entity Too Large"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - assert 413 == 401
 +  where 413 = <Response [413 Request Entity Too Large]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 0.83s
operator: core/ReplaceComparisonOperator_Gt_LtE, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -280,7 +280,7 @@
 
     # Check content length before reading body to prevent memory exhaustion
     content_length = request.headers.get("content-length")
-    if content_length and int(content_length) > MAX_WEBHOOK_BODY_SIZE:
+    if content_length and int(content_length) <= MAX_WEBHOOK_BODY_SIZE:
         errors_total.labels(error_type="payload_too_large").inc()
         raise HTTPException(status_code=413, detail="Request body too large")
 
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7fd8003b3770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
        response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )
        # Will fail signature validation, but Content-Length tracking happens before that
>       assert response.status_code == 401
E       assert 413 == 401
E        +  where 413 = <Response [413 Request Entity Too Large]>.status_code

tests/test_main.py:79: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:34:54.980981Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 413 Request Entity Too Large"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 413 Request Entity Too Large"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - assert 413 == 401
 +  where 413 = <Response [413 Request Entity Too Large]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 0.83s
operator: core/ReplaceComparisonOperator_Gt_LtE, occurrence: 1
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -288,7 +288,7 @@
     body = await request.body()
 
     # Double-check actual body size (in case content-length was missing/incorrect)
-    if len(body) > MAX_WEBHOOK_BODY_SIZE:
+    if len(body) <= MAX_WEBHOOK_BODY_SIZE:
         errors_total.labels(error_type="payload_too_large").inc()
         raise HTTPException(status_code=413, detail="Request body too large")
 
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7f75685af770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
        response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )
        # Will fail signature validation, but Content-Length tracking happens before that
>       assert response.status_code == 401
E       assert 413 == 401
E        +  where 413 = <Response [413 Request Entity Too Large]>.status_code

tests/test_main.py:79: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:49:06.179651Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 413 Request Entity Too Large"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 413 Request Entity Too Large"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - assert 413 == 401
 +  where 413 = <Response [413 Request Entity Too Large]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 0.84s
operator: core/ReplaceComparisonOperator_Gt_GtE, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -280,7 +280,7 @@
 
     # Check content length before reading body to prevent memory exhaustion
     content_length = request.headers.get("content-length")
-    if content_length and int(content_length) > MAX_WEBHOOK_BODY_SIZE:
+    if content_length and int(content_length) >= MAX_WEBHOOK_BODY_SIZE:
         errors_total.labels(error_type="payload_too_large").inc()
         raise HTTPException(status_code=413, detail="Request body too large")
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceComparisonOperator_Gt_GtE, occurrence: 1
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -288,7 +288,7 @@
     body = await request.body()
 
     # Double-check actual body size (in case content-length was missing/incorrect)
-    if len(body) > MAX_WEBHOOK_BODY_SIZE:
+    if len(body) >= MAX_WEBHOOK_BODY_SIZE:
         errors_total.labels(error_type="payload_too_large").inc()
         raise HTTPException(status_code=413, detail="Request body too large")
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceComparisonOperator_Gt_Is, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -280,7 +280,7 @@
 
     # Check content length before reading body to prevent memory exhaustion
     content_length = request.headers.get("content-length")
-    if content_length and int(content_length) > MAX_WEBHOOK_BODY_SIZE:
+    if content_length and int(content_length) is MAX_WEBHOOK_BODY_SIZE:
         errors_total.labels(error_type="payload_too_large").inc()
         raise HTTPException(status_code=413, detail="Request body too large")
 
........................................................................ [ 34%]
..............................F
=================================== FAILURES ===================================
____________________ test_webhook_content_length_too_large _____________________

test_client = <starlette.testclient.TestClient object at 0x7fca2f0d2f10>

    def test_webhook_content_length_too_large(test_client: TestClient):
        """Test webhook rejects requests with Content-Length exceeding limit."""
        response = test_client.post(
            "/webhook",
            content=b"x",  # Small actual body
            headers={
                "Content-Type": "application/json",
                "Content-Length": "20000000",  # 20MB - exceeds 10MB limit
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=fake",
            },
        )
>       assert response.status_code == 413
E       assert 401 == 413
E        +  where 401 = <Response [401 Unauthorized]>.status_code

tests/test_main.py:193: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:38:56.241468Z [warning  ] Invalid webhook signature      _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=warning client_ip=testclient
2026-03-27T13:38:56.242276Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 401 Unauthorized"
------------------------------ Captured log call -------------------------------
WARNING  stampbot.main:main.py:299 {'event': 'Invalid webhook signature', 'client_ip': 'testclient', 'level': 'warning', 'timestamp': '2026-03-27T13:38:56.241468Z'}
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 401 Unauthorized"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_content_length_too_large - assert 401 == 413
 +  where 401 = <Response [401 Unauthorized]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 102 passed, 3 deselected in 0.87s
operator: core/ReplaceComparisonOperator_Gt_Is, occurrence: 1
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -288,7 +288,7 @@
     body = await request.body()
 
     # Double-check actual body size (in case content-length was missing/incorrect)
-    if len(body) > MAX_WEBHOOK_BODY_SIZE:
+    if len(body) is MAX_WEBHOOK_BODY_SIZE:
         errors_total.labels(error_type="payload_too_large").inc()
         raise HTTPException(status_code=413, detail="Request body too large")
 
........................................................................ [ 34%]
...............................F
=================================== FAILURES ===================================
_________________________ test_webhook_body_too_large __________________________

test_client = <starlette.testclient.TestClient object at 0x7ff4694cf790>

    def test_webhook_body_too_large(test_client: TestClient):
        """Test webhook rejects requests where actual body exceeds limit.
    
        This tests the secondary body size check (lines 231-232) which catches
        cases where Content-Length header is missing or incorrect.
        """
        # Create a body larger than MAX_WEBHOOK_BODY_SIZE (10MB)
        large_body = b"x" * (10 * 1024 * 1024 + 1)  # 10MB + 1 byte
    
        # Set Content-Length to a small value to bypass the header check (line 222)
        # but the actual body size check (line 230) should still catch it
        response = test_client.post(
            "/webhook",
            content=large_body,
            headers={
                "Content-Type": "application/json",
                "Content-Length": "100",  # Lie about size to bypass first check
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=fake",
            },
        )
>       assert response.status_code == 413
E       assert 401 == 413
E        +  where 401 = <Response [401 Unauthorized]>.status_code

tests/test_main.py:218: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:40:45.739144Z [warning  ] Invalid webhook signature      _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=warning client_ip=testclient
2026-03-27T13:40:45.740249Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 401 Unauthorized"
------------------------------ Captured log call -------------------------------
WARNING  stampbot.main:main.py:299 {'event': 'Invalid webhook signature', 'client_ip': 'testclient', 'level': 'warning', 'timestamp': '2026-03-27T13:40:45.739144Z'}
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 401 Unauthorized"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_body_too_large - assert 401 == 413
 +  where 401 = <Response [401 Unauthorized]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 103 passed, 3 deselected in 0.88s
operator: core/ReplaceComparisonOperator_Gt_IsNot, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -280,7 +280,7 @@
 
     # Check content length before reading body to prevent memory exhaustion
     content_length = request.headers.get("content-length")
-    if content_length and int(content_length) > MAX_WEBHOOK_BODY_SIZE:
+    if content_length and int(content_length) is not MAX_WEBHOOK_BODY_SIZE:
         errors_total.labels(error_type="payload_too_large").inc()
         raise HTTPException(status_code=413, detail="Request body too large")
 
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7f303f7a7770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
        response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )
        # Will fail signature validation, but Content-Length tracking happens before that
>       assert response.status_code == 401
E       assert 413 == 401
E        +  where 413 = <Response [413 Request Entity Too Large]>.status_code

tests/test_main.py:79: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:50:56.579966Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 413 Request Entity Too Large"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 413 Request Entity Too Large"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - assert 413 == 401
 +  where 413 = <Response [413 Request Entity Too Large]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 0.83s
operator: core/ReplaceComparisonOperator_Gt_IsNot, occurrence: 1
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -288,7 +288,7 @@
     body = await request.body()
 
     # Double-check actual body size (in case content-length was missing/incorrect)
-    if len(body) > MAX_WEBHOOK_BODY_SIZE:
+    if len(body) is not MAX_WEBHOOK_BODY_SIZE:
         errors_total.labels(error_type="payload_too_large").inc()
         raise HTTPException(status_code=413, detail="Request body too large")
 
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7feef3cb7770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
        response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )
        # Will fail signature validation, but Content-Length tracking happens before that
>       assert response.status_code == 401
E       assert 413 == 401
E        +  where 413 = <Response [413 Request Entity Too Large]>.status_code

tests/test_main.py:79: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:56:20.397622Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 413 Request Entity Too Large"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 413 Request Entity Too Large"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - assert 413 == 401
 +  where 413 = <Response [413 Request Entity Too Large]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 0.84s
operator: core/ReplaceUnaryOperator_Delete_Not, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -71,7 +71,7 @@
     )
 
     # Log setup mode status
-    if not is_configured():
+    if  is_configured():
         logger.warning("GitHub App credentials not configured. Running in setup mode.")
         logger.info("Visit /setup to create your GitHub App")
     else:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/ReplaceUnaryOperator_Delete_Not, occurrence: 1
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -196,7 +196,7 @@
                     # entry is the original client (proxies append to the right).
                     client_ip = raw.split(",")[0].strip()
 
-            if not client_ip and scope.get("client"):
+            if  client_ip and scope.get("client"):
                 client_ip = scope["client"][0]
 
             if client_ip:
........................................................................ [ 34%]
...................................F
=================================== FAILURES ===================================
______________ test_logging_middleware_respects_configured_header ______________

    def test_logging_middleware_respects_configured_header():
        """Test that logging_middleware uses the configured client_ip_header setting."""
        from unittest.mock import patch
    
        import structlog.contextvars
    
        from stampbot.main import app
    
        with patch("stampbot.main.settings") as mock_settings:
            mock_settings.get = lambda key, default=None: (
                "X-Real-IP" if key == "client_ip_header" else default
            )
    
            client = TestClient(app)
            captured: dict[str, str | None] = {}
    
            original_bind = structlog.contextvars.bind_contextvars
    
            def capturing_bind(**kw: object) -> None:
                captured.update({k: str(v) for k, v in kw.items()})
                original_bind(**kw)
    
            with patch("stampbot.main.structlog.contextvars.bind_contextvars", capturing_bind):
                client.get("/health", headers={"X-Real-IP": "198.51.100.7"})
    
>           assert captured.get("client_ip") == "198.51.100.7"
E           AssertionError: assert 'testclient' == '198.51.100.7'
E             
E             - 198.51.100.7
E             + testclient

tests/test_main.py:296: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:08:33.076747Z [info     ] HTTP Request: GET http://testserver/health "HTTP/1.1 200 OK"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: GET http://testserver/health "HTTP/1.1 200 OK"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_logging_middleware_respects_configured_header - AssertionError: assert 'testclient' == '198.51.100.7'
  
  - 198.51.100.7
  + testclient
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 107 passed, 3 deselected in 0.87s
operator: core/ReplaceUnaryOperator_Delete_Not, occurrence: 2
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -215,7 +215,7 @@
     Returns:
         Redirect to /setup if unconfigured, otherwise JSON status response.
     """
-    if not is_configured() and settings.setup_enabled:
+    if  is_configured() and settings.setup_enabled:
         return RedirectResponse(url="/setup", status_code=307)
 
     return Response(
........................................................................ [ 34%]
.....................F
=================================== FAILURES ===================================
______________________________ test_root_endpoint ______________________________

test_client = <starlette.testclient.TestClient object at 0x7f7200ca4050>

    def test_root_endpoint(test_client: TestClient):
        """Test root endpoint returns basic info."""
        response = test_client.get("/")
        assert response.status_code == 200
>       data = response.json()
               ^^^^^^^^^^^^^^^

tests/test_main.py:46: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_models.py:832: in json
    return jsonlib.loads(self.content, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/json/__init__.py:352: in loads
    return _default_decoder.decode(s)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/json/decoder.py:345: in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <json.decoder.JSONDecoder object at 0x7f7208362f90>
s = '\n            <!DOCTYPE html>\n            <html>\n            <head><title>Stampbot - Already Configured</title>\n  ...pbot is ready to receive webhooks.</p>\n                </div>\n            </body>\n            </html>\n            '
idx = 13

    def raw_decode(self, s, idx=0):
        """Decode a JSON document from ``s`` (a ``str`` beginning with
        a JSON document) and return a 2-tuple of the Python
        representation and the index in ``s`` where the document ended.
    
        This can be used to decode a JSON document from a string that may
        have extraneous data at the end.
    
        """
        try:
            obj, end = self.scan_once(s, idx)
        except StopIteration as err:
>           raise JSONDecodeError("Expecting value", s, err.value) from None
E           json.decoder.JSONDecodeError: Expecting value: line 2 column 13 (char 13)

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/json/decoder.py:363: JSONDecodeError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:38:14.907642Z [info     ] HTTP Request: GET http://testserver/ "HTTP/1.1 307 Temporary Redirect"
2026-03-27T13:38:14.909904Z [info     ] HTTP Request: GET http://testserver/setup "HTTP/1.1 200 OK"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: GET http://testserver/ "HTTP/1.1 307 Temporary Redirect"
INFO     httpx:_client.py:1025 HTTP Request: GET http://testserver/setup "HTTP/1.1 200 OK"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_root_endpoint - json.decoder.JSONDecodeError: Expecting value: line 2 column 13 (char 13)
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 93 passed, 3 deselected in 0.90s
operator: core/ReplaceUnaryOperator_Delete_Not, occurrence: 3
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -268,7 +268,7 @@
             413 if body too large, 503 if not configured, 500 on internal error.
     """
     # Check if app is configured
-    if not is_configured():
+    if  is_configured():
         raise HTTPException(
             status_code=503,
             detail="Stampbot not configured. Visit /setup to complete setup.",
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7f4b9e57f770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
        response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )
        # Will fail signature validation, but Content-Length tracking happens before that
>       assert response.status_code == 401
E       assert 503 == 401
E        +  where 503 = <Response [503 Service Unavailable]>.status_code

tests/test_main.py:79: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:27:59.063165Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 503 Service Unavailable"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 503 Service Unavailable"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - assert 503 == 401
 +  where 503 = <Response [503 Service Unavailable]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 0.84s
operator: core/ReplaceUnaryOperator_Delete_Not, occurrence: 4
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -274,7 +274,7 @@
             detail="Stampbot not configured. Visit /setup to complete setup.",
         )
 
-    if not x_github_event:
+    if  x_github_event:
         errors_total.labels(error_type="missing_event").inc()
         raise HTTPException(status_code=400, detail="Missing X-GitHub-Event header")
 
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7f95fb39b770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
        response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )
        # Will fail signature validation, but Content-Length tracking happens before that
>       assert response.status_code == 401
E       assert 400 == 401
E        +  where 400 = <Response [400 Bad Request]>.status_code

tests/test_main.py:79: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:38:31.855428Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 400 Bad Request"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 400 Bad Request"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - assert 400 == 401
 +  where 400 = <Response [400 Bad Request]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 0.84s
operator: core/ReplaceUnaryOperator_Delete_Not, occurrence: 5
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -293,7 +293,7 @@
         raise HTTPException(status_code=413, detail="Request body too large")
 
     # Verify signature
-    if not webhook_handler.verify_signature(body, x_hub_signature_256):
+    if  webhook_handler.verify_signature(body, x_hub_signature_256):
         webhook_signature_validations_total.labels(result="invalid").inc()
         errors_total.labels(error_type="signature_invalid").inc()
         logger.warning("Invalid webhook signature")
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7f5e60d83770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
        response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )
        # Will fail signature validation, but Content-Length tracking happens before that
>       assert response.status_code == 401
E       assert 200 == 401
E        +  where 200 = <Response [200 OK]>.status_code

tests/test_main.py:79: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:06:55.388246Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-03-27T14:06:55.389199Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 200 OK"
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'ping', 'action': ''}, 'event': 'Received ping event', 'client_ip': 'testclient', 'level': 'info', 'timestamp': '2026-03-27T14:06:55.388246Z'}
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 200 OK"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - assert 200 == 401
 +  where 200 = <Response [200 OK]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 0.84s
operator: core/ReplaceUnaryOperator_Delete_Not, occurrence: 6
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -345,7 +345,7 @@
     Raises:
         HTTPException: If setup is disabled (403).
     """
-    if not settings.setup_enabled:
+    if  settings.setup_enabled:
         raise HTTPException(status_code=403, detail="Setup not allowed in this environment")
 
     if is_configured():
........................................................................ [ 34%]
...........................................................F
=================================== FAILURES ===================================
_______ TestSetupEndpointsConfigured.test_setup_shows_already_configured _______

self = <tests.test_setup_endpoints.TestSetupEndpointsConfigured object at 0x7f76cccef9d0>
test_client = <starlette.testclient.TestClient object at 0x7f76c842ead0>

    def test_setup_shows_already_configured(self, test_client):
        """Test /setup shows already configured message."""
        response = test_client.get("/setup")
    
>       assert response.status_code == 200
E       assert 403 == 200
E        +  where 403 = <Response [403 Forbidden]>.status_code

tests/test_setup_endpoints.py:22: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:05:44.377408Z [info     ] HTTP Request: GET http://testserver/setup "HTTP/1.1 403 Forbidden"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: GET http://testserver/setup "HTTP/1.1 403 Forbidden"
=========================== short test summary info ============================
FAILED tests/test_setup_endpoints.py::TestSetupEndpointsConfigured::test_setup_shows_already_configured - assert 403 == 200
 +  where 403 = <Response [403 Forbidden]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 131 passed, 3 deselected in 0.99s
operator: core/ReplaceUnaryOperator_Delete_Not, occurrence: 7
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -464,7 +464,7 @@
     Raises:
         HTTPException: If setup disabled (403) or code exchange fails (500).
     """
-    if not settings.setup_enabled:
+    if  settings.setup_enabled:
         raise HTTPException(status_code=403, detail="Setup not allowed")
 
     try:
........................................................................ [ 34%]
..................................................................F
=================================== FAILURES ===================================
________________ TestSetupCallback.test_callback_exchanges_code ________________

self = <tests.test_setup_endpoints.TestSetupCallback object at 0x7ff53c547d90>
test_client = <starlette.testclient.TestClient object at 0x7ff533fefac0>

    def test_callback_exchanges_code(self, test_client):
        """Test callback exchanges code for credentials."""
        mock_credentials = {
            "id": 12345,
            "pem": "-----BEGIN RSA PRIVATE KEY-----\ntest\n-----END RSA PRIVATE KEY-----",
            "webhook_secret": "test-secret",
            "slug": "test-stampbot",
            "name": "Test Stampbot",
        }
    
        with patch(
            "stampbot.main.exchange_code_for_credentials",
            new_callable=AsyncMock,
            return_value=mock_credentials,
        ):
            response = test_client.get("/setup/callback?code=test-code")
    
>           assert response.status_code == 200
E           assert 403 == 200
E            +  where 403 = <Response [403 Forbidden]>.status_code

tests/test_setup_endpoints.py:179: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:01:32.212653Z [info     ] HTTP Request: GET http://testserver/setup/callback?code=test-code "HTTP/1.1 403 Forbidden"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: GET http://testserver/setup/callback?code=test-code "HTTP/1.1 403 Forbidden"
=========================== short test summary info ============================
FAILED tests/test_setup_endpoints.py::TestSetupCallback::test_callback_exchanges_code - assert 403 == 200
 +  where 403 = <Response [403 Forbidden]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 138 passed, 3 deselected in 1.04s
operator: core/AddNot, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -71,7 +71,7 @@
     )
 
     # Log setup mode status
-    if not is_configured():
+    if not not is_configured():
         logger.warning("GitHub App credentials not configured. Running in setup mode.")
         logger.info("Visit /setup to create your GitHub App")
     else:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/AddNot, occurrence: 1
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -135,7 +135,7 @@
 
         # Track request size (from Content-Length header if available)
         content_length = request.headers.get("content-length")
-        if content_length:
+        if not content_length:
             http_request_size_bytes.labels(
                 method=method,
                 endpoint=endpoint,
........................................................................ [ 34%]
...................F
=================================== FAILURES ===================================
__________________ test_lifespan_startup_shutdown_configured ___________________
  + Exception Group Traceback (most recent call last):
  |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py", line 79, in collapse_excgroups
  |     yield
  |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py", line 192, in __call__
  |     async with anyio.create_task_group() as task_group:
  |                ~~~~~~~~~~~~~~~~~~~~~~~^^
  |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/_backends/_asyncio.py", line 783, in __aexit__
  |     raise BaseExceptionGroup(
  |         "unhandled errors in a TaskGroup", self._exceptions
  |     ) from None
  | ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
  +-+---------------- 1 ----------------
    | Traceback (most recent call last):
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/runner.py", line 353, in from_call
    |     result: TResult | None = func()
    |                              ~~~~^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/runner.py", line 245, in <lambda>
    |     lambda: runtest_hook(item=item, **kwds),
    |             ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_hooks.py", line 512, in __call__
    |     return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
    |            ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_manager.py", line 120, in _hookexec
    |     return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
    |            ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 167, in _multicall
    |     raise exception
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 139, in _multicall
    |     teardown.throw(exception)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/logging.py", line 850, in pytest_runtest_call
    |     yield
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 139, in _multicall
    |     teardown.throw(exception)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 53, in run_old_style_hookwrapper
    |     return result.get_result()
    |            ~~~~~~~~~~~~~~~~~^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_result.py", line 103, in get_result
    |     raise exc.with_traceback(tb)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 38, in run_old_style_hookwrapper
    |     res = yield
    |           ^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 139, in _multicall
    |     teardown.throw(exception)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/capture.py", line 900, in pytest_runtest_call
    |     return (yield)
    |             ^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 139, in _multicall
    |     teardown.throw(exception)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/skipping.py", line 268, in pytest_runtest_call
    |     return (yield)
    |             ^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 121, in _multicall
    |     res = hook_impl.function(*args)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/runner.py", line 179, in pytest_runtest_call
    |     item.runtest()
    |     ~~~~~~~~~~~~^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/python.py", line 1720, in runtest
    |     self.ihook.pytest_pyfunc_call(pyfuncitem=self)
    |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_hooks.py", line 512, in __call__
    |     return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
    |            ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_manager.py", line 120, in _hookexec
    |     return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
    |            ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 167, in _multicall
    |     raise exception
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 139, in _multicall
    |     teardown.throw(exception)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 53, in run_old_style_hookwrapper
    |     return result.get_result()
    |            ~~~~~~~~~~~~~~~~~^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_result.py", line 103, in get_result
    |     raise exc.with_traceback(tb)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 38, in run_old_style_hookwrapper
    |     res = yield
    |           ^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 121, in _multicall
    |     res = hook_impl.function(*args)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/python.py", line 166, in pytest_pyfunc_call
    |     result = testfunction(**testargs)
    |   File "/home/runner/work/stampbot/stampbot/tests/test_main.py", line 20, in test_lifespan_startup_shutdown_configured
    |     response = client.get("/health")
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 473, in get
    |     return super().get(
    |            ~~~~~~~~~~~^
    |         url,
    |         ^^^^
    |     ...<6 lines>...
    |         extensions=extensions,
    |         ^^^^^^^^^^^^^^^^^^^^^^
    |     )
    |     ^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 1053, in get
    |     return self.request(
    |            ~~~~~~~~~~~~^
    |         "GET",
    |         ^^^^^^
    |     ...<7 lines>...
    |         extensions=extensions,
    |         ^^^^^^^^^^^^^^^^^^^^^^
    |     )
    |     ^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 445, in request
    |     return super().request(
    |            ~~~~~~~~~~~~~~~^
    |         method,
    |         ^^^^^^^
    |     ...<11 lines>...
    |         extensions=extensions,
    |         ^^^^^^^^^^^^^^^^^^^^^^
    |     )
    |     ^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 825, in request
    |     return self.send(request, auth=auth, follow_redirects=follow_redirects)
    |            ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 914, in send
    |     response = self._send_handling_auth(
    |         request,
    |     ...<2 lines>...
    |         history=[],
    |     )
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 942, in _send_handling_auth
    |     response = self._send_handling_redirects(
    |         request,
    |         follow_redirects=follow_redirects,
    |         history=history,
    |     )
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 979, in _send_handling_redirects
    |     response = self._send_single_request(request)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 1014, in _send_single_request
    |     response = transport.handle_request(request)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 348, in handle_request
    |     raise exc
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 345, in handle_request
    |     portal.call(self.app, scope, receive, send)
    |     ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py", line 334, in call
    |     return cast(T_Retval, self.start_task_soon(func, *args).result())
    |                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
    |   File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py", line 450, in result
    |     return self.__get_result()
    |            ~~~~~~~~~~~~~~~~~^^
    |   File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py", line 395, in __get_result
    |     raise self._exception
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py", line 259, in _call_func
    |     retval = await retval_or_awaitable
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py", line 1135, in __call__
    |     await super().__call__(scope, receive, send)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py", line 107, in __call__
    |     await self.middleware_stack(scope, receive, send)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py", line 186, in __call__
    |     raise exc
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py", line 164, in __call__
    |     await self.app(scope, receive, _send)
    |   File "/home/runner/work/stampbot/stampbot/stampbot/main.py", line 205, in __call__
    |     await self.app(scope, receive, send)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py", line 191, in __call__
    |     with recv_stream, send_stream, collapse_excgroups():
    |                                    ~~~~~~~~~~~~~~~~~~^^
    |   File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py", line 162, in __exit__
    |     self.gen.throw(value)
    |     ~~~~~~~~~~~~~~^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py", line 85, in collapse_excgroups
    |     raise exc
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py", line 193, in __call__
    |     response = await self.dispatch_func(request, call_next)
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/work/stampbot/stampbot/stampbot/main.py", line 142, in metrics_middleware
    |     ).observe(int(content_length))
    |               ~~~^^^^^^^^^^^^^^^^
    | TypeError: int() argument must be a string, a bytes-like object or a real number, not 'NoneType'
    +------------------------------------

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/runner/work/stampbot/stampbot/tests/test_main.py", line 20, in test_lifespan_startup_shutdown_configured
    response = client.get("/health")
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 473, in get
    return super().get(
           ~~~~~~~~~~~^
        url,
        ^^^^
    ...<6 lines>...
        extensions=extensions,
        ^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 1053, in get
    return self.request(
           ~~~~~~~~~~~~^
        "GET",
        ^^^^^^
    ...<7 lines>...
        extensions=extensions,
        ^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 445, in request
    return super().request(
           ~~~~~~~~~~~~~~~^
        method,
        ^^^^^^^
    ...<11 lines>...
        extensions=extensions,
        ^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 825, in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 914, in send
    response = self._send_handling_auth(
        request,
    ...<2 lines>...
        history=[],
    )
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 942, in _send_handling_auth
    response = self._send_handling_redirects(
        request,
        follow_redirects=follow_redirects,
        history=history,
    )
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 979, in _send_handling_redirects
    response = self._send_single_request(request)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 1014, in _send_single_request
    response = transport.handle_request(request)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 348, in handle_request
    raise exc
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 345, in handle_request
    portal.call(self.app, scope, receive, send)
    ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py", line 334, in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
  File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py", line 450, in result
    return self.__get_result()
           ~~~~~~~~~~~~~~~~~^^
  File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py", line 395, in __get_result
    raise self._exception
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py", line 259, in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py", line 1135, in __call__
    await super().__call__(scope, receive, send)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py", line 107, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py", line 186, in __call__
    raise exc
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py", line 164, in __call__
    await self.app(scope, receive, _send)
  File "/home/runner/work/stampbot/stampbot/stampbot/main.py", line 205, in __call__
    await self.app(scope, receive, send)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py", line 191, in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ~~~~~~~~~~~~~~~~~~^^
  File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py", line 162, in __exit__
    self.gen.throw(value)
    ~~~~~~~~~~~~~~^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py", line 85, in collapse_excgroups
    raise exc
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py", line 193, in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/stampbot/stampbot/stampbot/main.py", line 142, in metrics_middleware
    ).observe(int(content_length))
              ~~~^^^^^^^^^^^^^^^^
TypeError: int() argument must be a string, a bytes-like object or a real number, not 'NoneType'

During handling of the above exception, another exception occurred:

    def test_lifespan_startup_shutdown_configured():
        """Test app lifespan startup and shutdown when configured."""
        from stampbot.main import app
    
        # Use TestClient as context manager to trigger lifespan events
        with TestClient(app) as client:
            # App should be running
>           response = client.get("/health")
                       ^^^^^^^^^^^^^^^^^^^^^

tests/test_main.py:20: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:473: in get
    return super().get(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1053: in get
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:450: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

request = <starlette.middleware.base._CachedRequest object at 0x7f0b456a2f90>
call_next = <function BaseHTTPMiddleware.__call__.<locals>.call_next at 0x7f0b456a6770>

    @app.middleware("http")
    async def metrics_middleware(request: Request, call_next: Any) -> Response:
        """Middleware to track HTTP metrics.
    
        Args:
            request: Incoming HTTP request.
            call_next: Next middleware or route handler.
    
        Returns:
            HTTP response from downstream handler.
        """
        method = request.method
        endpoint = request.url.path
    
        # Track in-progress requests
        http_requests_in_progress.labels(method=method, endpoint=endpoint).inc()
    
        start_time = time.time()
    
        try:
            response = await call_next(request)
    
            duration = time.time() - start_time
    
            # Track request metrics
            http_requests_total.labels(
                method=method,
                endpoint=endpoint,
                status=response.status_code,
            ).inc()
    
            http_request_duration_seconds.labels(
                method=method,
                endpoint=endpoint,
            ).observe(duration)
    
            # Track request size (from Content-Length header if available)
            content_length = request.headers.get("content-length")
            if not content_length:
                http_request_size_bytes.labels(
                    method=method,
                    endpoint=endpoint,
>               ).observe(int(content_length))
                          ^^^^^^^^^^^^^^^^^^^
E               TypeError: int() argument must be a string, a bytes-like object or a real number, not 'NoneType'

stampbot/main.py:142: TypeError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:40:20.716100Z [info     ] OpenTelemetry disabled         _logger=<_FixedFindCallerLogger stampbot.telemetry (INFO)> _name=info
2026-03-27T13:40:20.734766Z [info     ] Starting stampbot on 0.0.0.0:8000 _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=info extra={'host': '0.0.0.0', 'port': 8000, 'log_level': 'INFO'}
2026-03-27T13:40:20.735079Z [info     ] GitHub App credentials configured successfully _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=info
2026-03-27T13:40:20.737337Z [info     ] Shutting down stampbot         _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=info
=========================== short test summary info ============================
FAILED tests/test_main.py::test_lifespan_startup_shutdown_configured - TypeError: int() argument must be a string, a bytes-like object or a real number, not 'NoneType'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 91 passed, 3 deselected in 1.03s
operator: core/AddNot, occurrence: 2
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -143,7 +143,7 @@
 
         # Track response size
         response_size = response.headers.get("content-length")
-        if response_size:
+        if not response_size:
             http_response_size_bytes.labels(
                 method=method,
                 endpoint=endpoint,
........................................................................ [ 34%]
................................F
=================================== FAILURES ===================================
_____________________ test_response_without_content_length _____________________
  + Exception Group Traceback (most recent call last):
  |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py", line 79, in collapse_excgroups
  |     yield
  |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py", line 192, in __call__
  |     async with anyio.create_task_group() as task_group:
  |                ~~~~~~~~~~~~~~~~~~~~~~~^^
  |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/_backends/_asyncio.py", line 783, in __aexit__
  |     raise BaseExceptionGroup(
  |         "unhandled errors in a TaskGroup", self._exceptions
  |     ) from None
  | ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
  +-+---------------- 1 ----------------
    | Traceback (most recent call last):
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/runner.py", line 353, in from_call
    |     result: TResult | None = func()
    |                              ~~~~^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/runner.py", line 245, in <lambda>
    |     lambda: runtest_hook(item=item, **kwds),
    |             ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_hooks.py", line 512, in __call__
    |     return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
    |            ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_manager.py", line 120, in _hookexec
    |     return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
    |            ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 167, in _multicall
    |     raise exception
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 139, in _multicall
    |     teardown.throw(exception)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/logging.py", line 850, in pytest_runtest_call
    |     yield
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 139, in _multicall
    |     teardown.throw(exception)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 53, in run_old_style_hookwrapper
    |     return result.get_result()
    |            ~~~~~~~~~~~~~~~~~^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_result.py", line 103, in get_result
    |     raise exc.with_traceback(tb)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 38, in run_old_style_hookwrapper
    |     res = yield
    |           ^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 139, in _multicall
    |     teardown.throw(exception)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/capture.py", line 900, in pytest_runtest_call
    |     return (yield)
    |             ^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 139, in _multicall
    |     teardown.throw(exception)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/skipping.py", line 268, in pytest_runtest_call
    |     return (yield)
    |             ^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 121, in _multicall
    |     res = hook_impl.function(*args)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/runner.py", line 179, in pytest_runtest_call
    |     item.runtest()
    |     ~~~~~~~~~~~~^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/python.py", line 1720, in runtest
    |     self.ihook.pytest_pyfunc_call(pyfuncitem=self)
    |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_hooks.py", line 512, in __call__
    |     return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
    |            ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_manager.py", line 120, in _hookexec
    |     return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
    |            ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 167, in _multicall
    |     raise exception
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 139, in _multicall
    |     teardown.throw(exception)
    |     ~~~~~~~~~~~~~~^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 53, in run_old_style_hookwrapper
    |     return result.get_result()
    |            ~~~~~~~~~~~~~~~~~^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_result.py", line 103, in get_result
    |     raise exc.with_traceback(tb)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 38, in run_old_style_hookwrapper
    |     res = yield
    |           ^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/pluggy/_callers.py", line 121, in _multicall
    |     res = hook_impl.function(*args)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/_pytest/python.py", line 166, in pytest_pyfunc_call
    |     result = testfunction(**testargs)
    |   File "/home/runner/work/stampbot/stampbot/tests/test_main.py", line 247, in test_response_without_content_length
    |     response = client.get(streaming_route_path)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 473, in get
    |     return super().get(
    |            ~~~~~~~~~~~^
    |         url,
    |         ^^^^
    |     ...<6 lines>...
    |         extensions=extensions,
    |         ^^^^^^^^^^^^^^^^^^^^^^
    |     )
    |     ^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 1053, in get
    |     return self.request(
    |            ~~~~~~~~~~~~^
    |         "GET",
    |         ^^^^^^
    |     ...<7 lines>...
    |         extensions=extensions,
    |         ^^^^^^^^^^^^^^^^^^^^^^
    |     )
    |     ^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 445, in request
    |     return super().request(
    |            ~~~~~~~~~~~~~~~^
    |         method,
    |         ^^^^^^^
    |     ...<11 lines>...
    |         extensions=extensions,
    |         ^^^^^^^^^^^^^^^^^^^^^^
    |     )
    |     ^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 825, in request
    |     return self.send(request, auth=auth, follow_redirects=follow_redirects)
    |            ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 914, in send
    |     response = self._send_handling_auth(
    |         request,
    |     ...<2 lines>...
    |         history=[],
    |     )
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 942, in _send_handling_auth
    |     response = self._send_handling_redirects(
    |         request,
    |         follow_redirects=follow_redirects,
    |         history=history,
    |     )
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 979, in _send_handling_redirects
    |     response = self._send_single_request(request)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 1014, in _send_single_request
    |     response = transport.handle_request(request)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 348, in handle_request
    |     raise exc
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 345, in handle_request
    |     portal.call(self.app, scope, receive, send)
    |     ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py", line 334, in call
    |     return cast(T_Retval, self.start_task_soon(func, *args).result())
    |                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
    |   File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py", line 450, in result
    |     return self.__get_result()
    |            ~~~~~~~~~~~~~~~~~^^
    |   File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py", line 395, in __get_result
    |     raise self._exception
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py", line 259, in _call_func
    |     retval = await retval_or_awaitable
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py", line 1135, in __call__
    |     await super().__call__(scope, receive, send)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py", line 107, in __call__
    |     await self.middleware_stack(scope, receive, send)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py", line 186, in __call__
    |     raise exc
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py", line 164, in __call__
    |     await self.app(scope, receive, _send)
    |   File "/home/runner/work/stampbot/stampbot/stampbot/main.py", line 205, in __call__
    |     await self.app(scope, receive, send)
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py", line 191, in __call__
    |     with recv_stream, send_stream, collapse_excgroups():
    |                                    ~~~~~~~~~~~~~~~~~~^^
    |   File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py", line 162, in __exit__
    |     self.gen.throw(value)
    |     ~~~~~~~~~~~~~~^^^^^^^
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py", line 85, in collapse_excgroups
    |     raise exc
    |   File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py", line 193, in __call__
    |     response = await self.dispatch_func(request, call_next)
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/home/runner/work/stampbot/stampbot/stampbot/main.py", line 150, in metrics_middleware
    |     ).observe(int(response_size))
    |               ~~~^^^^^^^^^^^^^^^
    | TypeError: int() argument must be a string, a bytes-like object or a real number, not 'NoneType'
    +------------------------------------

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/runner/work/stampbot/stampbot/tests/test_main.py", line 247, in test_response_without_content_length
    response = client.get(streaming_route_path)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 473, in get
    return super().get(
           ~~~~~~~~~~~^
        url,
        ^^^^
    ...<6 lines>...
        extensions=extensions,
        ^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 1053, in get
    return self.request(
           ~~~~~~~~~~~~^
        "GET",
        ^^^^^^
    ...<7 lines>...
        extensions=extensions,
        ^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 445, in request
    return super().request(
           ~~~~~~~~~~~~~~~^
        method,
        ^^^^^^^
    ...<11 lines>...
        extensions=extensions,
        ^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 825, in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 914, in send
    response = self._send_handling_auth(
        request,
    ...<2 lines>...
        history=[],
    )
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 942, in _send_handling_auth
    response = self._send_handling_redirects(
        request,
        follow_redirects=follow_redirects,
        history=history,
    )
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 979, in _send_handling_redirects
    response = self._send_single_request(request)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py", line 1014, in _send_single_request
    response = transport.handle_request(request)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 348, in handle_request
    raise exc
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py", line 345, in handle_request
    portal.call(self.app, scope, receive, send)
    ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py", line 334, in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
  File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py", line 450, in result
    return self.__get_result()
           ~~~~~~~~~~~~~~~~~^^
  File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py", line 395, in __get_result
    raise self._exception
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py", line 259, in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py", line 1135, in __call__
    await super().__call__(scope, receive, send)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py", line 107, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py", line 186, in __call__
    raise exc
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py", line 164, in __call__
    await self.app(scope, receive, _send)
  File "/home/runner/work/stampbot/stampbot/stampbot/main.py", line 205, in __call__
    await self.app(scope, receive, send)
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py", line 191, in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ~~~~~~~~~~~~~~~~~~^^
  File "/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py", line 162, in __exit__
    self.gen.throw(value)
    ~~~~~~~~~~~~~~^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py", line 85, in collapse_excgroups
    raise exc
  File "/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py", line 193, in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/stampbot/stampbot/stampbot/main.py", line 150, in metrics_middleware
    ).observe(int(response_size))
              ~~~^^^^^^^^^^^^^^^
TypeError: int() argument must be a string, a bytes-like object or a real number, not 'NoneType'

During handling of the above exception, another exception occurred:

    def test_response_without_content_length():
        """Test that responses without Content-Length header are handled correctly.
    
        This tests the branch where response_size is None (line 142->148).
        StreamingResponse doesn't include Content-Length header.
        """
        from collections.abc import AsyncIterator
    
        from fastapi.responses import StreamingResponse
    
        from stampbot.main import app
    
        # Track if our streaming endpoint was added
        streaming_route_path = "/_test_streaming_no_cl"
    
        async def generate() -> AsyncIterator[bytes]:
            yield b"chunk1"
            yield b"chunk2"
    
        # Add a temporary streaming endpoint
        @app.get(streaming_route_path)
        async def streaming_no_content_length() -> StreamingResponse:
            return StreamingResponse(generate(), media_type="text/plain")
    
        client = TestClient(app)
>       response = client.get(streaming_route_path)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_main.py:247: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:473: in get
    return super().get(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1053: in get
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:450: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

request = <starlette.middleware.base._CachedRequest object at 0x7f1633d1edd0>
call_next = <function BaseHTTPMiddleware.__call__.<locals>.call_next at 0x7f1633d15220>

    @app.middleware("http")
    async def metrics_middleware(request: Request, call_next: Any) -> Response:
        """Middleware to track HTTP metrics.
    
        Args:
            request: Incoming HTTP request.
            call_next: Next middleware or route handler.
    
        Returns:
            HTTP response from downstream handler.
        """
        method = request.method
        endpoint = request.url.path
    
        # Track in-progress requests
        http_requests_in_progress.labels(method=method, endpoint=endpoint).inc()
    
        start_time = time.time()
    
        try:
            response = await call_next(request)
    
            duration = time.time() - start_time
    
            # Track request metrics
            http_requests_total.labels(
                method=method,
                endpoint=endpoint,
                status=response.status_code,
            ).inc()
    
            http_request_duration_seconds.labels(
                method=method,
                endpoint=endpoint,
            ).observe(duration)
    
            # Track request size (from Content-Length header if available)
            content_length = request.headers.get("content-length")
            if content_length:
                http_request_size_bytes.labels(
                    method=method,
                    endpoint=endpoint,
                ).observe(int(content_length))
    
            # Track response size
            response_size = response.headers.get("content-length")
            if not response_size:
                http_response_size_bytes.labels(
                    method=method,
                    endpoint=endpoint,
>               ).observe(int(response_size))
                          ^^^^^^^^^^^^^^^^^^
E               TypeError: int() argument must be a string, a bytes-like object or a real number, not 'NoneType'

stampbot/main.py:150: TypeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_response_without_content_length - TypeError: int() argument must be a string, a bytes-like object or a real number, not 'NoneType'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 104 passed, 3 deselected in 1.11s
operator: core/AddNot, occurrence: 3
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -183,7 +183,7 @@
             receive: ASGI receive callable.
             send: ASGI send callable.
         """
-        if scope["type"] == "http":
+        if not scope["type"] == "http":
             structlog.contextvars.clear_contextvars()
 
             client_ip: str | None = None
........................................................................ [ 34%]
...................F
=================================== FAILURES ===================================
__________________ test_lifespan_startup_shutdown_configured ___________________

    def test_lifespan_startup_shutdown_configured():
        """Test app lifespan startup and shutdown when configured."""
        from stampbot.main import app
    
        # Use TestClient as context manager to trigger lifespan events
>       with TestClient(app) as client:
             ^^^^^^^^^^^^^^^

tests/test_main.py:18: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:688: in __enter__
    portal.call(self.wait_startup)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:717: in wait_startup
    message = await receive()
              ^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:714: in receive
    self.task.result()
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:704: in lifespan
    await self.app(scope, self.stream_receive.receive, self.stream_send.send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:151: in __call__
    await self.app(scope, receive, send)
stampbot/main.py:193: in __call__
    raw = Request(scope).headers.get(client_ip_header)
          ^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/requests.py:204: in __init__
    super().__init__(scope)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <starlette.requests.Request object at 0x7f65d68a27b0>
scope = {'app': <fastapi.applications.FastAPI object at 0x7f65d6ad6f90>, 'state': {}, 'type': 'lifespan'}
receive = None

    def __init__(self, scope: Scope, receive: Receive | None = None) -> None:
>       assert scope["type"] in ("http", "websocket")
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E       AssertionError

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/requests.py:78: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:28:39.111377Z [info     ] OpenTelemetry disabled         _logger=<_FixedFindCallerLogger stampbot.telemetry (INFO)> _name=info
=========================== short test summary info ============================
FAILED tests/test_main.py::test_lifespan_startup_shutdown_configured - AssertionError
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 91 passed, 3 deselected in 0.96s
operator: core/AddNot, occurrence: 4
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -189,7 +189,7 @@
             client_ip: str | None = None
             client_ip_header: str = settings.get("client_ip_header", "X-Forwarded-For")
 
-            if client_ip_header:
+            if not client_ip_header:
                 raw = Request(scope).headers.get(client_ip_header)
                 if raw:
                     # X-Forwarded-For may be a comma-separated list; the leftmost
........................................................................ [ 34%]
...................................F
=================================== FAILURES ===================================
______________ test_logging_middleware_respects_configured_header ______________

    def test_logging_middleware_respects_configured_header():
        """Test that logging_middleware uses the configured client_ip_header setting."""
        from unittest.mock import patch
    
        import structlog.contextvars
    
        from stampbot.main import app
    
        with patch("stampbot.main.settings") as mock_settings:
            mock_settings.get = lambda key, default=None: (
                "X-Real-IP" if key == "client_ip_header" else default
            )
    
            client = TestClient(app)
            captured: dict[str, str | None] = {}
    
            original_bind = structlog.contextvars.bind_contextvars
    
            def capturing_bind(**kw: object) -> None:
                captured.update({k: str(v) for k, v in kw.items()})
                original_bind(**kw)
    
            with patch("stampbot.main.structlog.contextvars.bind_contextvars", capturing_bind):
                client.get("/health", headers={"X-Real-IP": "198.51.100.7"})
    
>           assert captured.get("client_ip") == "198.51.100.7"
E           AssertionError: assert 'testclient' == '198.51.100.7'
E             
E             - 198.51.100.7
E             + testclient

tests/test_main.py:296: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:11:16.738138Z [info     ] HTTP Request: GET http://testserver/health "HTTP/1.1 200 OK"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: GET http://testserver/health "HTTP/1.1 200 OK"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_logging_middleware_respects_configured_header - AssertionError: assert 'testclient' == '198.51.100.7'
  
  - 198.51.100.7
  + testclient
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 107 passed, 3 deselected in 0.88s
operator: core/AddNot, occurrence: 5
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -191,7 +191,7 @@
 
             if client_ip_header:
                 raw = Request(scope).headers.get(client_ip_header)
-                if raw:
+                if not raw:
                     # X-Forwarded-For may be a comma-separated list; the leftmost
                     # entry is the original client (proxies append to the right).
                     client_ip = raw.split(",")[0].strip()
........................................................................ [ 34%]
...................F
=================================== FAILURES ===================================
__________________ test_lifespan_startup_shutdown_configured ___________________

    def test_lifespan_startup_shutdown_configured():
        """Test app lifespan startup and shutdown when configured."""
        from stampbot.main import app
    
        # Use TestClient as context manager to trigger lifespan events
        with TestClient(app) as client:
            # App should be running
>           response = client.get("/health")
                       ^^^^^^^^^^^^^^^^^^^^^

tests/test_main.py:20: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:473: in get
    return super().get(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1053: in get
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.main.LoggingMiddleware object at 0x7f87fdf8a510>
scope = {'app': <fastapi.applications.FastAPI object at 0x7f87fe1bef90>, 'client': ('testclient', 50000), 'extensions': {'http... b'*/*'), (b'accept-encoding', b'gzip, deflate'), (b'connection', b'keep-alive'), (b'user-agent', b'testclient')], ...}
receive = <function _TestClientTransport.handle_request.<locals>.receive at 0x7f87fdf8dfe0>
send = <function ServerErrorMiddleware.__call__.<locals>._send at 0x7f87fdf8e610>

    async def __call__(self, scope: Any, receive: Any, send: Any) -> None:
        """Process an ASGI event, binding client IP to the structlog context.
    
        For HTTP scopes the middleware clears any inherited structlog context,
        extracts the real client IP from the configured forwarding header (or
        the direct connection address as a fallback), and binds it so that
        every log record emitted during the request includes ``client_ip``.
    
        Non-HTTP scopes (lifespan, WebSocket) are passed through unchanged.
    
        Args:
            scope: ASGI connection scope.
            receive: ASGI receive callable.
            send: ASGI send callable.
        """
        if scope["type"] == "http":
            structlog.contextvars.clear_contextvars()
    
            client_ip: str | None = None
            client_ip_header: str = settings.get("client_ip_header", "X-Forwarded-For")
    
            if client_ip_header:
                raw = Request(scope).headers.get(client_ip_header)
                if not raw:
                    # X-Forwarded-For may be a comma-separated list; the leftmost
                    # entry is the original client (proxies append to the right).
>                   client_ip = raw.split(",")[0].strip()
                                ^^^^^^^^^
E                   AttributeError: 'NoneType' object has no attribute 'split'

stampbot/main.py:197: AttributeError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:53:51.575396Z [info     ] OpenTelemetry disabled         _logger=<_FixedFindCallerLogger stampbot.telemetry (INFO)> _name=info
2026-03-27T13:53:51.594551Z [info     ] Starting stampbot on 0.0.0.0:8000 _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=info extra={'host': '0.0.0.0', 'port': 8000, 'log_level': 'INFO'}
2026-03-27T13:53:51.594916Z [info     ] GitHub App credentials configured successfully _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=info
2026-03-27T13:53:51.596171Z [info     ] Shutting down stampbot         _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=info
=========================== short test summary info ============================
FAILED tests/test_main.py::test_lifespan_startup_shutdown_configured - AttributeError: 'NoneType' object has no attribute 'split'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 91 passed, 3 deselected in 1.03s
operator: core/AddNot, occurrence: 6
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -196,7 +196,7 @@
                     # entry is the original client (proxies append to the right).
                     client_ip = raw.split(",")[0].strip()
 
-            if not client_ip and scope.get("client"):
+            if not not client_ip and scope.get("client"):
                 client_ip = scope["client"][0]
 
             if client_ip:
........................................................................ [ 34%]
...................................F
=================================== FAILURES ===================================
______________ test_logging_middleware_respects_configured_header ______________

    def test_logging_middleware_respects_configured_header():
        """Test that logging_middleware uses the configured client_ip_header setting."""
        from unittest.mock import patch
    
        import structlog.contextvars
    
        from stampbot.main import app
    
        with patch("stampbot.main.settings") as mock_settings:
            mock_settings.get = lambda key, default=None: (
                "X-Real-IP" if key == "client_ip_header" else default
            )
    
            client = TestClient(app)
            captured: dict[str, str | None] = {}
    
            original_bind = structlog.contextvars.bind_contextvars
    
            def capturing_bind(**kw: object) -> None:
                captured.update({k: str(v) for k, v in kw.items()})
                original_bind(**kw)
    
            with patch("stampbot.main.structlog.contextvars.bind_contextvars", capturing_bind):
                client.get("/health", headers={"X-Real-IP": "198.51.100.7"})
    
>           assert captured.get("client_ip") == "198.51.100.7"
E           AssertionError: assert 'testclient' == '198.51.100.7'
E             
E             - 198.51.100.7
E             + testclient

tests/test_main.py:296: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:57:39.848299Z [info     ] HTTP Request: GET http://testserver/health "HTTP/1.1 200 OK"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: GET http://testserver/health "HTTP/1.1 200 OK"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_logging_middleware_respects_configured_header - AssertionError: assert 'testclient' == '198.51.100.7'
  
  - 198.51.100.7
  + testclient
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 107 passed, 3 deselected in 0.88s
operator: core/AddNot, occurrence: 7
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -199,7 +199,7 @@
             if not client_ip and scope.get("client"):
                 client_ip = scope["client"][0]
 
-            if client_ip:
+            if not client_ip:
                 structlog.contextvars.bind_contextvars(client_ip=client_ip)
 
         await self.app(scope, receive, send)
........................................................................ [ 34%]
...................................F
=================================== FAILURES ===================================
______________ test_logging_middleware_respects_configured_header ______________

    def test_logging_middleware_respects_configured_header():
        """Test that logging_middleware uses the configured client_ip_header setting."""
        from unittest.mock import patch
    
        import structlog.contextvars
    
        from stampbot.main import app
    
        with patch("stampbot.main.settings") as mock_settings:
            mock_settings.get = lambda key, default=None: (
                "X-Real-IP" if key == "client_ip_header" else default
            )
    
            client = TestClient(app)
            captured: dict[str, str | None] = {}
    
            original_bind = structlog.contextvars.bind_contextvars
    
            def capturing_bind(**kw: object) -> None:
                captured.update({k: str(v) for k, v in kw.items()})
                original_bind(**kw)
    
            with patch("stampbot.main.structlog.contextvars.bind_contextvars", capturing_bind):
                client.get("/health", headers={"X-Real-IP": "198.51.100.7"})
    
>           assert captured.get("client_ip") == "198.51.100.7"
E           AssertionError: assert None == '198.51.100.7'
E            +  where None = <built-in method get of dict object at 0x7fbc2bf1e3c0>('client_ip')
E            +    where <built-in method get of dict object at 0x7fbc2bf1e3c0> = {}.get

tests/test_main.py:296: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:26:27.943757Z [info     ] HTTP Request: GET http://testserver/health "HTTP/1.1 200 OK"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: GET http://testserver/health "HTTP/1.1 200 OK"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_logging_middleware_respects_configured_header - AssertionError: assert None == '198.51.100.7'
 +  where None = <built-in method get of dict object at 0x7fbc2bf1e3c0>('client_ip')
 +    where <built-in method get of dict object at 0x7fbc2bf1e3c0> = {}.get
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 107 passed, 3 deselected in 0.88s
operator: core/AddNot, occurrence: 8
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -215,7 +215,7 @@
     Returns:
         Redirect to /setup if unconfigured, otherwise JSON status response.
     """
-    if not is_configured() and settings.setup_enabled:
+    if not not is_configured() and settings.setup_enabled:
         return RedirectResponse(url="/setup", status_code=307)
 
     return Response(
........................................................................ [ 34%]
.....................F
=================================== FAILURES ===================================
______________________________ test_root_endpoint ______________________________

test_client = <starlette.testclient.TestClient object at 0x7f8386580050>

    def test_root_endpoint(test_client: TestClient):
        """Test root endpoint returns basic info."""
        response = test_client.get("/")
        assert response.status_code == 200
>       data = response.json()
               ^^^^^^^^^^^^^^^

tests/test_main.py:46: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_models.py:832: in json
    return jsonlib.loads(self.content, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/json/__init__.py:352: in loads
    return _default_decoder.decode(s)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/json/decoder.py:345: in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <json.decoder.JSONDecoder object at 0x7f838dc66f90>
s = '\n            <!DOCTYPE html>\n            <html>\n            <head><title>Stampbot - Already Configured</title>\n  ...pbot is ready to receive webhooks.</p>\n                </div>\n            </body>\n            </html>\n            '
idx = 13

    def raw_decode(self, s, idx=0):
        """Decode a JSON document from ``s`` (a ``str`` beginning with
        a JSON document) and return a 2-tuple of the Python
        representation and the index in ``s`` where the document ended.
    
        This can be used to decode a JSON document from a string that may
        have extraneous data at the end.
    
        """
        try:
            obj, end = self.scan_once(s, idx)
        except StopIteration as err:
>           raise JSONDecodeError("Expecting value", s, err.value) from None
E           json.decoder.JSONDecodeError: Expecting value: line 2 column 13 (char 13)

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/json/decoder.py:363: JSONDecodeError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:41:03.937833Z [info     ] HTTP Request: GET http://testserver/ "HTTP/1.1 307 Temporary Redirect"
2026-03-27T13:41:03.940290Z [info     ] HTTP Request: GET http://testserver/setup "HTTP/1.1 200 OK"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: GET http://testserver/ "HTTP/1.1 307 Temporary Redirect"
INFO     httpx:_client.py:1025 HTTP Request: GET http://testserver/setup "HTTP/1.1 200 OK"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_root_endpoint - json.decoder.JSONDecodeError: Expecting value: line 2 column 13 (char 13)
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 93 passed, 3 deselected in 0.86s
operator: core/AddNot, occurrence: 9
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -268,7 +268,7 @@
             413 if body too large, 503 if not configured, 500 on internal error.
     """
     # Check if app is configured
-    if not is_configured():
+    if not not is_configured():
         raise HTTPException(
             status_code=503,
             detail="Stampbot not configured. Visit /setup to complete setup.",
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7f20620a7770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
        response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )
        # Will fail signature validation, but Content-Length tracking happens before that
>       assert response.status_code == 401
E       assert 503 == 401
E        +  where 503 = <Response [503 Service Unavailable]>.status_code

tests/test_main.py:79: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:56:14.402734Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 503 Service Unavailable"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 503 Service Unavailable"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - assert 503 == 401
 +  where 503 = <Response [503 Service Unavailable]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 0.84s
operator: core/AddNot, occurrence: 10
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -274,7 +274,7 @@
             detail="Stampbot not configured. Visit /setup to complete setup.",
         )
 
-    if not x_github_event:
+    if not not x_github_event:
         errors_total.labels(error_type="missing_event").inc()
         raise HTTPException(status_code=400, detail="Missing X-GitHub-Event header")
 
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7f53628b3770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
        response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )
        # Will fail signature validation, but Content-Length tracking happens before that
>       assert response.status_code == 401
E       assert 400 == 401
E        +  where 400 = <Response [400 Bad Request]>.status_code

tests/test_main.py:79: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:41:02.045670Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 400 Bad Request"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 400 Bad Request"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - assert 400 == 401
 +  where 400 = <Response [400 Bad Request]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 0.84s
operator: core/AddNot, occurrence: 11
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -280,7 +280,7 @@
 
     # Check content length before reading body to prevent memory exhaustion
     content_length = request.headers.get("content-length")
-    if content_length and int(content_length) > MAX_WEBHOOK_BODY_SIZE:
+    if not content_length and int(content_length) > MAX_WEBHOOK_BODY_SIZE:
         errors_total.labels(error_type="payload_too_large").inc()
         raise HTTPException(status_code=413, detail="Request body too large")
 
........................................................................ [ 34%]
..............................F
=================================== FAILURES ===================================
____________________ test_webhook_content_length_too_large _____________________

test_client = <starlette.testclient.TestClient object at 0x7fb3b5cdef10>

    def test_webhook_content_length_too_large(test_client: TestClient):
        """Test webhook rejects requests with Content-Length exceeding limit."""
        response = test_client.post(
            "/webhook",
            content=b"x",  # Small actual body
            headers={
                "Content-Type": "application/json",
                "Content-Length": "20000000",  # 20MB - exceeds 10MB limit
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=fake",
            },
        )
>       assert response.status_code == 413
E       assert 401 == 413
E        +  where 401 = <Response [401 Unauthorized]>.status_code

tests/test_main.py:193: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:27:51.760916Z [warning  ] Invalid webhook signature      _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=warning client_ip=testclient
2026-03-27T13:27:51.761673Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 401 Unauthorized"
------------------------------ Captured log call -------------------------------
WARNING  stampbot.main:main.py:299 {'event': 'Invalid webhook signature', 'client_ip': 'testclient', 'level': 'warning', 'timestamp': '2026-03-27T13:27:51.760916Z'}
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 401 Unauthorized"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_content_length_too_large - assert 401 == 413
 +  where 401 = <Response [401 Unauthorized]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 102 passed, 3 deselected in 0.84s
operator: core/AddNot, occurrence: 12
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -288,7 +288,7 @@
     body = await request.body()
 
     # Double-check actual body size (in case content-length was missing/incorrect)
-    if len(body) > MAX_WEBHOOK_BODY_SIZE:
+    if not len(body) > MAX_WEBHOOK_BODY_SIZE:
         errors_total.labels(error_type="payload_too_large").inc()
         raise HTTPException(status_code=413, detail="Request body too large")
 
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7f7669c9f770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
        response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )
        # Will fail signature validation, but Content-Length tracking happens before that
>       assert response.status_code == 401
E       assert 413 == 401
E        +  where 413 = <Response [413 Request Entity Too Large]>.status_code

tests/test_main.py:79: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:07:14.664874Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 413 Request Entity Too Large"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 413 Request Entity Too Large"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - assert 413 == 401
 +  where 413 = <Response [413 Request Entity Too Large]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 0.84s
operator: core/AddNot, occurrence: 13
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -293,7 +293,7 @@
         raise HTTPException(status_code=413, detail="Request body too large")
 
     # Verify signature
-    if not webhook_handler.verify_signature(body, x_hub_signature_256):
+    if not not webhook_handler.verify_signature(body, x_hub_signature_256):
         webhook_signature_validations_total.labels(result="invalid").inc()
         errors_total.labels(error_type="signature_invalid").inc()
         logger.warning("Invalid webhook signature")
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7fd0555b3770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
        response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )
        # Will fail signature validation, but Content-Length tracking happens before that
>       assert response.status_code == 401
E       assert 200 == 401
E        +  where 200 = <Response [200 OK]>.status_code

tests/test_main.py:79: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:42:38.588065Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-03-27T13:42:38.589021Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 200 OK"
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'ping', 'action': ''}, 'event': 'Received ping event', 'client_ip': 'testclient', 'level': 'info', 'timestamp': '2026-03-27T13:42:38.588065Z'}
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 200 OK"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - assert 200 == 401
 +  where 200 = <Response [200 OK]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 0.83s
operator: core/AddNot, occurrence: 14
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -345,7 +345,7 @@
     Raises:
         HTTPException: If setup is disabled (403).
     """
-    if not settings.setup_enabled:
+    if not not settings.setup_enabled:
         raise HTTPException(status_code=403, detail="Setup not allowed in this environment")
 
     if is_configured():
........................................................................ [ 34%]
...........................................................F
=================================== FAILURES ===================================
_______ TestSetupEndpointsConfigured.test_setup_shows_already_configured _______

self = <tests.test_setup_endpoints.TestSetupEndpointsConfigured object at 0x7fd4104a79d0>
test_client = <starlette.testclient.TestClient object at 0x7fd407106ad0>

    def test_setup_shows_already_configured(self, test_client):
        """Test /setup shows already configured message."""
        response = test_client.get("/setup")
    
>       assert response.status_code == 200
E       assert 403 == 200
E        +  where 403 = <Response [403 Forbidden]>.status_code

tests/test_setup_endpoints.py:22: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:46:54.398892Z [info     ] HTTP Request: GET http://testserver/setup "HTTP/1.1 403 Forbidden"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: GET http://testserver/setup "HTTP/1.1 403 Forbidden"
=========================== short test summary info ============================
FAILED tests/test_setup_endpoints.py::TestSetupEndpointsConfigured::test_setup_shows_already_configured - assert 403 == 200
 +  where 403 = <Response [403 Forbidden]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 131 passed, 3 deselected in 0.99s
operator: core/AddNot, occurrence: 15
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -348,7 +348,7 @@
     if not settings.setup_enabled:
         raise HTTPException(status_code=403, detail="Setup not allowed in this environment")
 
-    if is_configured():
+    if not is_configured():
         return HTMLResponse(
             content="""
             <!DOCTYPE html>
........................................................................ [ 34%]
...........................................................F
=================================== FAILURES ===================================
_______ TestSetupEndpointsConfigured.test_setup_shows_already_configured _______

self = <tests.test_setup_endpoints.TestSetupEndpointsConfigured object at 0x7f4cd2adb9d0>
test_client = <starlette.testclient.TestClient object at 0x7f4ccd81ead0>

    def test_setup_shows_already_configured(self, test_client):
        """Test /setup shows already configured message."""
>       response = test_client.get("/setup")
                   ^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_setup_endpoints.py:20: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:473: in get
    return super().get(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1053: in get
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:450: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:396: in setup_page
    manifest = create_manifest(redirect_url, webhook_url=webhook_url)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/manifest.py:85: in create_manifest
    _validate_url(redirect_url, "redirect_url")
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

url = 'http://testserver/setup/callback', name = 'redirect_url'

    def _validate_url(url: str, name: str) -> None:
        """Validate that a URL is well-formed and uses HTTPS (or localhost).
    
        Args:
            url: URL to validate
            name: Name of the URL parameter for error messages
    
        Raises:
            ValueError: If URL is invalid or doesn't use HTTPS
        """
        parsed = urlparse(url)
    
        if not parsed.scheme or not parsed.netloc:
            raise ValueError(f"Invalid {name}: must be a complete URL")
    
        # Allow http only for localhost (development)
        if parsed.scheme == "http":
            if parsed.hostname not in ("localhost", "127.0.0.1", "::1"):
>               raise ValueError(f"Invalid {name}: must use HTTPS (HTTP only allowed for localhost)")
E               ValueError: Invalid redirect_url: must use HTTPS (HTTP only allowed for localhost)

stampbot/manifest.py:36: ValueError
=========================== short test summary info ============================
FAILED tests/test_setup_endpoints.py::TestSetupEndpointsConfigured::test_setup_shows_already_configured - ValueError: Invalid redirect_url: must use HTTPS (HTTP only allowed for localhost)
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 131 passed, 3 deselected in 1.31s
operator: core/AddNot, occurrence: 16
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -379,7 +379,7 @@
     # 2. Proxy headers (X-Forwarded-Proto + Host/X-Forwarded-Host)
     # 3. Request base URL (fallback)
     configured_base_url = settings.get("base_url", "")
-    if configured_base_url:
+    if not configured_base_url:
         base_url = configured_base_url.rstrip("/")
     else:
         # Check for proxy headers from ingress/load balancer
........................................................................ [ 34%]
..............................................................F
=================================== FAILURES ===================================
___ TestSetupEndpointsUnconfigured.test_setup_shows_wizard_when_unconfigured ___

self = <tests.test_setup_endpoints.TestSetupEndpointsUnconfigured object at 0x7f6cda57fc50>

    def test_setup_shows_wizard_when_unconfigured(self):
        """Test /setup shows setup wizard page when not configured."""
        with (
            patch("stampbot.main.is_configured", return_value=False),
            patch("stampbot.main.settings") as mock_settings,
        ):
            mock_settings.setup_enabled = True
            mock_settings.get.return_value = ""  # No base_url override
    
            from fastapi.testclient import TestClient
    
            from stampbot.main import app
    
            client = TestClient(app, raise_server_exceptions=False)
            response = client.get("/setup", headers={"Host": "localhost:8000"})
    
>           assert response.status_code == 200
E           assert 500 == 200
E            +  where 500 = <Response [500 Internal Server Error]>.status_code

tests/test_setup_endpoints.py:75: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:28:31.867204Z [info     ] HTTP Request: GET http://testserver/setup "HTTP/1.1 500 Internal Server Error"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: GET http://testserver/setup "HTTP/1.1 500 Internal Server Error"
=========================== short test summary info ============================
FAILED tests/test_setup_endpoints.py::TestSetupEndpointsUnconfigured::test_setup_shows_wizard_when_unconfigured - assert 500 == 200
 +  where 500 = <Response [500 Internal Server Error]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 134 passed, 3 deselected in 1.01s
operator: core/AddNot, occurrence: 17
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -386,7 +386,7 @@
         # Cloud Run sends X-Forwarded-Proto but uses standard Host header
         forwarded_proto = request.headers.get("X-Forwarded-Proto", "")
         forwarded_host = request.headers.get("X-Forwarded-Host", request.headers.get("Host", ""))
-        if forwarded_proto and forwarded_host:
+        if not forwarded_proto and forwarded_host:
             base_url = f"{forwarded_proto}://{forwarded_host}"
         else:
             base_url = str(request.base_url).rstrip("/")
........................................................................ [ 34%]
..............................................................F
=================================== FAILURES ===================================
___ TestSetupEndpointsUnconfigured.test_setup_shows_wizard_when_unconfigured ___

self = <tests.test_setup_endpoints.TestSetupEndpointsUnconfigured object at 0x7f827ec63c50>

    def test_setup_shows_wizard_when_unconfigured(self):
        """Test /setup shows setup wizard page when not configured."""
        with (
            patch("stampbot.main.is_configured", return_value=False),
            patch("stampbot.main.settings") as mock_settings,
        ):
            mock_settings.setup_enabled = True
            mock_settings.get.return_value = ""  # No base_url override
    
            from fastapi.testclient import TestClient
    
            from stampbot.main import app
    
            client = TestClient(app, raise_server_exceptions=False)
            response = client.get("/setup", headers={"Host": "localhost:8000"})
    
>           assert response.status_code == 200
E           assert 500 == 200
E            +  where 500 = <Response [500 Internal Server Error]>.status_code

tests/test_setup_endpoints.py:75: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:34:23.384380Z [info     ] HTTP Request: GET http://testserver/setup "HTTP/1.1 500 Internal Server Error"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: GET http://testserver/setup "HTTP/1.1 500 Internal Server Error"
=========================== short test summary info ============================
FAILED tests/test_setup_endpoints.py::TestSetupEndpointsUnconfigured::test_setup_shows_wizard_when_unconfigured - assert 500 == 200
 +  where 500 = <Response [500 Internal Server Error]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 134 passed, 3 deselected in 1.01s
operator: core/AddNot, occurrence: 18
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -464,7 +464,7 @@
     Raises:
         HTTPException: If setup disabled (403) or code exchange fails (500).
     """
-    if not settings.setup_enabled:
+    if not not settings.setup_enabled:
         raise HTTPException(status_code=403, detail="Setup not allowed")
 
     try:
........................................................................ [ 34%]
..................................................................F
=================================== FAILURES ===================================
________________ TestSetupCallback.test_callback_exchanges_code ________________

self = <tests.test_setup_endpoints.TestSetupCallback object at 0x7f5bd2ccfd90>
test_client = <starlette.testclient.TestClient object at 0x7f5bce623ac0>

    def test_callback_exchanges_code(self, test_client):
        """Test callback exchanges code for credentials."""
        mock_credentials = {
            "id": 12345,
            "pem": "-----BEGIN RSA PRIVATE KEY-----\ntest\n-----END RSA PRIVATE KEY-----",
            "webhook_secret": "test-secret",
            "slug": "test-stampbot",
            "name": "Test Stampbot",
        }
    
        with patch(
            "stampbot.main.exchange_code_for_credentials",
            new_callable=AsyncMock,
            return_value=mock_credentials,
        ):
            response = test_client.get("/setup/callback?code=test-code")
    
>           assert response.status_code == 200
E           assert 403 == 200
E            +  where 403 = <Response [403 Forbidden]>.status_code

tests/test_setup_endpoints.py:179: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:41:30.283039Z [info     ] HTTP Request: GET http://testserver/setup/callback?code=test-code "HTTP/1.1 403 Forbidden"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: GET http://testserver/setup/callback?code=test-code "HTTP/1.1 403 Forbidden"
=========================== short test summary info ============================
FAILED tests/test_setup_endpoints.py::TestSetupCallback::test_callback_exchanges_code - assert 403 == 200
 +  where 403 = <Response [403 Forbidden]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 138 passed, 3 deselected in 1.02s
operator: core/AddNot, occurrence: 19
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -573,6 +573,6 @@
     return {
         "configured": is_configured(),
         "setup_enabled": settings.setup_enabled,
-        "app_id": settings.app_id if is_configured() else None,
+        "app_id": settings.app_id if not is_configured() else None,
     }
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceAndWithOr, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -196,7 +196,7 @@
                     # entry is the original client (proxies append to the right).
                     client_ip = raw.split(",")[0].strip()
 
-            if not client_ip and scope.get("client"):
+            if not client_ip or scope.get("client"):
                 client_ip = scope["client"][0]
 
             if client_ip:
........................................................................ [ 34%]
...................................F
=================================== FAILURES ===================================
______________ test_logging_middleware_respects_configured_header ______________

    def test_logging_middleware_respects_configured_header():
        """Test that logging_middleware uses the configured client_ip_header setting."""
        from unittest.mock import patch
    
        import structlog.contextvars
    
        from stampbot.main import app
    
        with patch("stampbot.main.settings") as mock_settings:
            mock_settings.get = lambda key, default=None: (
                "X-Real-IP" if key == "client_ip_header" else default
            )
    
            client = TestClient(app)
            captured: dict[str, str | None] = {}
    
            original_bind = structlog.contextvars.bind_contextvars
    
            def capturing_bind(**kw: object) -> None:
                captured.update({k: str(v) for k, v in kw.items()})
                original_bind(**kw)
    
            with patch("stampbot.main.structlog.contextvars.bind_contextvars", capturing_bind):
                client.get("/health", headers={"X-Real-IP": "198.51.100.7"})
    
>           assert captured.get("client_ip") == "198.51.100.7"
E           AssertionError: assert 'testclient' == '198.51.100.7'
E             
E             - 198.51.100.7
E             + testclient

tests/test_main.py:296: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:01:23.252476Z [info     ] HTTP Request: GET http://testserver/health "HTTP/1.1 200 OK"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: GET http://testserver/health "HTTP/1.1 200 OK"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_logging_middleware_respects_configured_header - AssertionError: assert 'testclient' == '198.51.100.7'
  
  - 198.51.100.7
  + testclient
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 107 passed, 3 deselected in 0.87s
operator: core/ReplaceAndWithOr, occurrence: 1
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -215,7 +215,7 @@
     Returns:
         Redirect to /setup if unconfigured, otherwise JSON status response.
     """
-    if not is_configured() and settings.setup_enabled:
+    if not is_configured() or settings.setup_enabled:
         return RedirectResponse(url="/setup", status_code=307)
 
     return Response(
........................................................................ [ 34%]
.....................F
=================================== FAILURES ===================================
______________________________ test_root_endpoint ______________________________

test_client = <starlette.testclient.TestClient object at 0x7fd797b98050>

    def test_root_endpoint(test_client: TestClient):
        """Test root endpoint returns basic info."""
        response = test_client.get("/")
        assert response.status_code == 200
>       data = response.json()
               ^^^^^^^^^^^^^^^

tests/test_main.py:46: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_models.py:832: in json
    return jsonlib.loads(self.content, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/json/__init__.py:352: in loads
    return _default_decoder.decode(s)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/json/decoder.py:345: in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <json.decoder.JSONDecoder object at 0x7fd79f266f90>
s = '\n            <!DOCTYPE html>\n            <html>\n            <head><title>Stampbot - Already Configured</title>\n  ...pbot is ready to receive webhooks.</p>\n                </div>\n            </body>\n            </html>\n            '
idx = 13

    def raw_decode(self, s, idx=0):
        """Decode a JSON document from ``s`` (a ``str`` beginning with
        a JSON document) and return a 2-tuple of the Python
        representation and the index in ``s`` where the document ended.
    
        This can be used to decode a JSON document from a string that may
        have extraneous data at the end.
    
        """
        try:
            obj, end = self.scan_once(s, idx)
        except StopIteration as err:
>           raise JSONDecodeError("Expecting value", s, err.value) from None
E           json.decoder.JSONDecodeError: Expecting value: line 2 column 13 (char 13)

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/json/decoder.py:363: JSONDecodeError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:08:44.095621Z [info     ] HTTP Request: GET http://testserver/ "HTTP/1.1 307 Temporary Redirect"
2026-03-27T14:08:44.097952Z [info     ] HTTP Request: GET http://testserver/setup "HTTP/1.1 200 OK"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: GET http://testserver/ "HTTP/1.1 307 Temporary Redirect"
INFO     httpx:_client.py:1025 HTTP Request: GET http://testserver/setup "HTTP/1.1 200 OK"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_root_endpoint - json.decoder.JSONDecodeError: Expecting value: line 2 column 13 (char 13)
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 93 passed, 3 deselected in 0.85s
operator: core/ReplaceAndWithOr, occurrence: 2
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -280,7 +280,7 @@
 
     # Check content length before reading body to prevent memory exhaustion
     content_length = request.headers.get("content-length")
-    if content_length and int(content_length) > MAX_WEBHOOK_BODY_SIZE:
+    if content_length or int(content_length) > MAX_WEBHOOK_BODY_SIZE:
         errors_total.labels(error_type="payload_too_large").inc()
         raise HTTPException(status_code=413, detail="Request body too large")
 
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7f3c35ca7770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
        response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )
        # Will fail signature validation, but Content-Length tracking happens before that
>       assert response.status_code == 401
E       assert 413 == 401
E        +  where 413 = <Response [413 Request Entity Too Large]>.status_code

tests/test_main.py:79: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:00:16.285138Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 413 Request Entity Too Large"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 413 Request Entity Too Large"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - assert 413 == 401
 +  where 413 = <Response [413 Request Entity Too Large]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 0.84s
operator: core/ReplaceAndWithOr, occurrence: 3
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -386,7 +386,7 @@
         # Cloud Run sends X-Forwarded-Proto but uses standard Host header
         forwarded_proto = request.headers.get("X-Forwarded-Proto", "")
         forwarded_host = request.headers.get("X-Forwarded-Host", request.headers.get("Host", ""))
-        if forwarded_proto and forwarded_host:
+        if forwarded_proto or forwarded_host:
             base_url = f"{forwarded_proto}://{forwarded_host}"
         else:
             base_url = str(request.base_url).rstrip("/")
........................................................................ [ 34%]
..............................................................F
=================================== FAILURES ===================================
___ TestSetupEndpointsUnconfigured.test_setup_shows_wizard_when_unconfigured ___

self = <tests.test_setup_endpoints.TestSetupEndpointsUnconfigured object at 0x7fe8c1f0fc50>

    def test_setup_shows_wizard_when_unconfigured(self):
        """Test /setup shows setup wizard page when not configured."""
        with (
            patch("stampbot.main.is_configured", return_value=False),
            patch("stampbot.main.settings") as mock_settings,
        ):
            mock_settings.setup_enabled = True
            mock_settings.get.return_value = ""  # No base_url override
    
            from fastapi.testclient import TestClient
    
            from stampbot.main import app
    
            client = TestClient(app, raise_server_exceptions=False)
            response = client.get("/setup", headers={"Host": "localhost:8000"})
    
>           assert response.status_code == 200
E           assert 500 == 200
E            +  where 500 = <Response [500 Internal Server Error]>.status_code

tests/test_setup_endpoints.py:75: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:59:07.252008Z [info     ] HTTP Request: GET http://testserver/setup "HTTP/1.1 500 Internal Server Error"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: GET http://testserver/setup "HTTP/1.1 500 Internal Server Error"
=========================== short test summary info ============================
FAILED tests/test_setup_endpoints.py::TestSetupEndpointsUnconfigured::test_setup_shows_wizard_when_unconfigured - assert 500 == 200
 +  where 500 = <Response [500 Internal Server Error]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 134 passed, 3 deselected in 1.01s
operator: core/ReplaceOrWithAnd, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -315,7 +315,7 @@
         result = await webhook_handler.handle_event(x_github_event, payload)
         duration = time.time() - start_time
 
-        webhook_processing_duration_seconds.labels(event_type=x_github_event or "unknown").observe(
+        webhook_processing_duration_seconds.labels(event_type=x_github_event and "unknown").observe(
             duration
         )
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ExceptionReplacer, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -304,7 +304,7 @@
     # Parse JSON payload
     try:
         payload = await request.json()
-    except Exception as e:
+    except CosmicRayTestingException as e:
         errors_total.labels(error_type="payload_invalid").inc()
         logger.error("Failed to parse webhook payload: %s", e)
         raise HTTPException(status_code=400, detail="Invalid JSON payload") from None
........................................................................ [ 34%]
............................F
=================================== FAILURES ===================================
__________________________ test_webhook_invalid_json ___________________________

request = <starlette.requests.Request object at 0x7f0f007bb070>
x_github_event = 'ping'
x_hub_signature_256 = 'sha256=dc481541cb8394ab16852fc7c7cbce720b045e7456801ae3998a45f260ad2915'

    @app.post("/webhook")
    async def webhook(
        request: Request,
        x_github_event: str = Header(None, alias="X-GitHub-Event"),
        x_hub_signature_256: str = Header(None, alias="X-Hub-Signature-256"),
    ) -> dict[str, Any]:
        """GitHub webhook endpoint.
    
        Args:
            request: FastAPI request.
            x_github_event: GitHub event type.
            x_hub_signature_256: Webhook signature.
    
        Returns:
            Response dictionary with status and message.
    
        Raises:
            HTTPException: 400 if event header or JSON invalid, 401 if signature invalid,
                413 if body too large, 503 if not configured, 500 on internal error.
        """
        # Check if app is configured
        if not is_configured():
            raise HTTPException(
                status_code=503,
                detail="Stampbot not configured. Visit /setup to complete setup.",
            )
    
        if not x_github_event:
            errors_total.labels(error_type="missing_event").inc()
            raise HTTPException(status_code=400, detail="Missing X-GitHub-Event header")
    
        # Check content length before reading body to prevent memory exhaustion
        content_length = request.headers.get("content-length")
        if content_length and int(content_length) > MAX_WEBHOOK_BODY_SIZE:
            errors_total.labels(error_type="payload_too_large").inc()
            raise HTTPException(status_code=413, detail="Request body too large")
    
        # Get raw body for signature verification
        body = await request.body()
    
        # Double-check actual body size (in case content-length was missing/incorrect)
        if len(body) > MAX_WEBHOOK_BODY_SIZE:
            errors_total.labels(error_type="payload_too_large").inc()
            raise HTTPException(status_code=413, detail="Request body too large")
    
        # Verify signature
        if not webhook_handler.verify_signature(body, x_hub_signature_256):
            webhook_signature_validations_total.labels(result="invalid").inc()
            errors_total.labels(error_type="signature_invalid").inc()
            logger.warning("Invalid webhook signature")
            raise HTTPException(status_code=401, detail="Invalid signature")
    
        webhook_signature_validations_total.labels(result="valid").inc()
    
        # Parse JSON payload
        try:
>           payload = await request.json()
                      ^^^^^^^^^^^^^^^^^^^^

stampbot/main.py:306: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/requests.py:251: in json
    self._json = json.loads(body)
                 ^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/json/__init__.py:352: in loads
    return _default_decoder.decode(s)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/json/decoder.py:345: in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <json.decoder.JSONDecoder object at 0x7f0f07d62f90>, s = 'not valid json'
idx = 0

    def raw_decode(self, s, idx=0):
        """Decode a JSON document from ``s`` (a ``str`` beginning with
        a JSON document) and return a 2-tuple of the Python
        representation and the index in ``s`` where the document ended.
    
        This can be used to decode a JSON document from a string that may
        have extraneous data at the end.
    
        """
        try:
            obj, end = self.scan_once(s, idx)
        except StopIteration as err:
>           raise JSONDecodeError("Expecting value", s, err.value) from None
E           json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/json/decoder.py:363: JSONDecodeError

The above exception was the direct cause of the following exception:

test_client = <starlette.testclient.TestClient object at 0x7f0f007ae580>

    def test_webhook_invalid_json(test_client: TestClient):
        """Test webhook rejects invalid JSON payload."""
        import hashlib
        import hmac
    
        from stampbot.webhook_handler import webhook_handler
    
        # Create a valid signature for invalid JSON
        body = b"not valid json"
        signature = (
            "sha256="
            + hmac.new(
                webhook_handler.webhook_secret,
                body,
                hashlib.sha256,
            ).hexdigest()
        )
    
>       response = test_client.post(
            "/webhook",
            content=body,
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": signature,
            },
        )

tests/test_main.py:136: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:546: in post
    return super().post(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1144: in post
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

request = <starlette.requests.Request object at 0x7f0f007bb070>
x_github_event = 'ping'
x_hub_signature_256 = 'sha256=dc481541cb8394ab16852fc7c7cbce720b045e7456801ae3998a45f260ad2915'

    @app.post("/webhook")
    async def webhook(
        request: Request,
        x_github_event: str = Header(None, alias="X-GitHub-Event"),
        x_hub_signature_256: str = Header(None, alias="X-Hub-Signature-256"),
    ) -> dict[str, Any]:
        """GitHub webhook endpoint.
    
        Args:
            request: FastAPI request.
            x_github_event: GitHub event type.
            x_hub_signature_256: Webhook signature.
    
        Returns:
            Response dictionary with status and message.
    
        Raises:
            HTTPException: 400 if event header or JSON invalid, 401 if signature invalid,
                413 if body too large, 503 if not configured, 500 on internal error.
        """
        # Check if app is configured
        if not is_configured():
            raise HTTPException(
                status_code=503,
                detail="Stampbot not configured. Visit /setup to complete setup.",
            )
    
        if not x_github_event:
            errors_total.labels(error_type="missing_event").inc()
            raise HTTPException(status_code=400, detail="Missing X-GitHub-Event header")
    
        # Check content length before reading body to prevent memory exhaustion
        content_length = request.headers.get("content-length")
        if content_length and int(content_length) > MAX_WEBHOOK_BODY_SIZE:
            errors_total.labels(error_type="payload_too_large").inc()
            raise HTTPException(status_code=413, detail="Request body too large")
    
        # Get raw body for signature verification
        body = await request.body()
    
        # Double-check actual body size (in case content-length was missing/incorrect)
        if len(body) > MAX_WEBHOOK_BODY_SIZE:
            errors_total.labels(error_type="payload_too_large").inc()
            raise HTTPException(status_code=413, detail="Request body too large")
    
        # Verify signature
        if not webhook_handler.verify_signature(body, x_hub_signature_256):
            webhook_signature_validations_total.labels(result="invalid").inc()
            errors_total.labels(error_type="signature_invalid").inc()
            logger.warning("Invalid webhook signature")
            raise HTTPException(status_code=401, detail="Invalid signature")
    
        webhook_signature_validations_total.labels(result="valid").inc()
    
        # Parse JSON payload
        try:
            payload = await request.json()
>       except CosmicRayTestingException as e:
               ^^^^^^^^^^^^^^^^^^^^^^^^^
E       NameError: name 'CosmicRayTestingException' is not defined

stampbot/main.py:307: NameError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_invalid_json - NameError: name 'CosmicRayTestingException' is not defined
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 100 passed, 3 deselected in 1.19s
operator: core/ExceptionReplacer, occurrence: 1
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -320,7 +320,7 @@
         )
 
         return result
-    except Exception as e:
+    except CosmicRayTestingException as e:
         errors_total.labels(error_type="webhook_handler_error").inc()
         sanitized = _sanitize_error(e)
         logger.error("Error handling webhook event: %s", sanitized, extra={"error": sanitized})
........................................................................ [ 34%]
.....................................F
=================================== FAILURES ===================================
________________________ test_webhook_handler_exception ________________________

request = <starlette.requests.Request object at 0x7f5e7b574890>
x_github_event = 'ping'
x_hub_signature_256 = 'sha256=595bde46b32458346f405d2b0b88ade07c42ddd464fa4cb8df965915cfa558d2'

    @app.post("/webhook")
    async def webhook(
        request: Request,
        x_github_event: str = Header(None, alias="X-GitHub-Event"),
        x_hub_signature_256: str = Header(None, alias="X-Hub-Signature-256"),
    ) -> dict[str, Any]:
        """GitHub webhook endpoint.
    
        Args:
            request: FastAPI request.
            x_github_event: GitHub event type.
            x_hub_signature_256: Webhook signature.
    
        Returns:
            Response dictionary with status and message.
    
        Raises:
            HTTPException: 400 if event header or JSON invalid, 401 if signature invalid,
                413 if body too large, 503 if not configured, 500 on internal error.
        """
        # Check if app is configured
        if not is_configured():
            raise HTTPException(
                status_code=503,
                detail="Stampbot not configured. Visit /setup to complete setup.",
            )
    
        if not x_github_event:
            errors_total.labels(error_type="missing_event").inc()
            raise HTTPException(status_code=400, detail="Missing X-GitHub-Event header")
    
        # Check content length before reading body to prevent memory exhaustion
        content_length = request.headers.get("content-length")
        if content_length and int(content_length) > MAX_WEBHOOK_BODY_SIZE:
            errors_total.labels(error_type="payload_too_large").inc()
            raise HTTPException(status_code=413, detail="Request body too large")
    
        # Get raw body for signature verification
        body = await request.body()
    
        # Double-check actual body size (in case content-length was missing/incorrect)
        if len(body) > MAX_WEBHOOK_BODY_SIZE:
            errors_total.labels(error_type="payload_too_large").inc()
            raise HTTPException(status_code=413, detail="Request body too large")
    
        # Verify signature
        if not webhook_handler.verify_signature(body, x_hub_signature_256):
            webhook_signature_validations_total.labels(result="invalid").inc()
            errors_total.labels(error_type="signature_invalid").inc()
            logger.warning("Invalid webhook signature")
            raise HTTPException(status_code=401, detail="Invalid signature")
    
        webhook_signature_validations_total.labels(result="valid").inc()
    
        # Parse JSON payload
        try:
            payload = await request.json()
        except Exception as e:
            errors_total.labels(error_type="payload_invalid").inc()
            logger.error("Failed to parse webhook payload: %s", e)
            raise HTTPException(status_code=400, detail="Invalid JSON payload") from None
    
        # Handle event with timing
        try:
            start_time = time.time()
>           result = await webhook_handler.handle_event(x_github_event, payload)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/main.py:315: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <AsyncMock name='handle_event' id='140043772904848'>
args = ('ping', {'zen': 'test'}), kwargs = {}
_call = call('ping', {'zen': 'test'}), effect = Exception('Unexpected error')

    async def _execute_mock_call(self, /, *args, **kwargs):
        # This is nearly just like super(), except for special handling
        # of coroutines
    
        _call = _Call((args, kwargs), two=True)
        self.await_count += 1
        self.await_args = _call
        self.await_args_list.append(_call)
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: Unexpected error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:2326: Exception

The above exception was the direct cause of the following exception:

test_client = <starlette.testclient.TestClient object at 0x7f5e7b6dff00>

    def test_webhook_handler_exception(test_client: TestClient):
        """Test webhook returns 500 when handler raises exception."""
        from stampbot.webhook_handler import webhook_handler
    
        body = json.dumps({"zen": "test"}).encode()
        signature = (
            "sha256="
            + hmac.new(
                webhook_handler.webhook_secret,
                body,
                hashlib.sha256,
            ).hexdigest()
        )
    
        with patch("stampbot.main.webhook_handler.handle_event") as mock_handle:
            mock_handle.side_effect = Exception("Unexpected error")
    
>           response = test_client.post(
                "/webhook",
                content=body,
                headers={
                    "Content-Type": "application/json",
                    "X-GitHub-Event": "ping",
                    "X-Hub-Signature-256": signature,
                },
            )

tests/test_main.py:350: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:546: in post
    return super().post(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1144: in post
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

request = <starlette.requests.Request object at 0x7f5e7b574890>
x_github_event = 'ping'
x_hub_signature_256 = 'sha256=595bde46b32458346f405d2b0b88ade07c42ddd464fa4cb8df965915cfa558d2'

    @app.post("/webhook")
    async def webhook(
        request: Request,
        x_github_event: str = Header(None, alias="X-GitHub-Event"),
        x_hub_signature_256: str = Header(None, alias="X-Hub-Signature-256"),
    ) -> dict[str, Any]:
        """GitHub webhook endpoint.
    
        Args:
            request: FastAPI request.
            x_github_event: GitHub event type.
            x_hub_signature_256: Webhook signature.
    
        Returns:
            Response dictionary with status and message.
    
        Raises:
            HTTPException: 400 if event header or JSON invalid, 401 if signature invalid,
                413 if body too large, 503 if not configured, 500 on internal error.
        """
        # Check if app is configured
        if not is_configured():
            raise HTTPException(
                status_code=503,
                detail="Stampbot not configured. Visit /setup to complete setup.",
            )
    
        if not x_github_event:
            errors_total.labels(error_type="missing_event").inc()
            raise HTTPException(status_code=400, detail="Missing X-GitHub-Event header")
    
        # Check content length before reading body to prevent memory exhaustion
        content_length = request.headers.get("content-length")
        if content_length and int(content_length) > MAX_WEBHOOK_BODY_SIZE:
            errors_total.labels(error_type="payload_too_large").inc()
            raise HTTPException(status_code=413, detail="Request body too large")
    
        # Get raw body for signature verification
        body = await request.body()
    
        # Double-check actual body size (in case content-length was missing/incorrect)
        if len(body) > MAX_WEBHOOK_BODY_SIZE:
            errors_total.labels(error_type="payload_too_large").inc()
            raise HTTPException(status_code=413, detail="Request body too large")
    
        # Verify signature
        if not webhook_handler.verify_signature(body, x_hub_signature_256):
            webhook_signature_validations_total.labels(result="invalid").inc()
            errors_total.labels(error_type="signature_invalid").inc()
            logger.warning("Invalid webhook signature")
            raise HTTPException(status_code=401, detail="Invalid signature")
    
        webhook_signature_validations_total.labels(result="valid").inc()
    
        # Parse JSON payload
        try:
            payload = await request.json()
        except Exception as e:
            errors_total.labels(error_type="payload_invalid").inc()
            logger.error("Failed to parse webhook payload: %s", e)
            raise HTTPException(status_code=400, detail="Invalid JSON payload") from None
    
        # Handle event with timing
        try:
            start_time = time.time()
            result = await webhook_handler.handle_event(x_github_event, payload)
            duration = time.time() - start_time
    
            webhook_processing_duration_seconds.labels(event_type=x_github_event or "unknown").observe(
                duration
            )
    
            return result
>       except CosmicRayTestingException as e:
               ^^^^^^^^^^^^^^^^^^^^^^^^^
E       NameError: name 'CosmicRayTestingException' is not defined

stampbot/main.py:323: NameError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_handler_exception - NameError: name 'CosmicRayTestingException' is not defined
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 109 passed, 3 deselected in 1.25s
operator: core/ExceptionReplacer, occurrence: 2
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -469,7 +469,7 @@
 
     try:
         credentials = await exchange_code_for_credentials(code)
-    except Exception as e:
+    except CosmicRayTestingException as e:
         logger.error("Failed to exchange code for credentials: %s", _sanitize_error(e))
         raise HTTPException(status_code=500, detail="Failed to complete setup") from None
 
........................................................................ [ 34%]
....................................................................F
=================================== FAILURES ===================================
____________ TestSetupCallback.test_callback_handles_exchange_error ____________

request = <starlette.requests.Request object at 0x7fcdb5b00e10>
code = 'bad-code'

    @app.get("/setup/callback")
    async def setup_callback(request: Request, code: str) -> Response:
        """Handle callback from GitHub after app creation.
    
        Args:
            request: FastAPI request.
            code: Temporary code from GitHub to exchange for credentials.
    
        Returns:
            HTML page with credentials and setup instructions.
    
        Raises:
            HTTPException: If setup disabled (403) or code exchange fails (500).
        """
        if not settings.setup_enabled:
            raise HTTPException(status_code=403, detail="Setup not allowed")
    
        try:
>           credentials = await exchange_code_for_credentials(code)
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/main.py:471: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <AsyncMock name='exchange_code_for_credentials' id='140521422368992'>
args = ('bad-code',), kwargs = {}, _call = call('bad-code')
effect = Exception('API error')

    async def _execute_mock_call(self, /, *args, **kwargs):
        # This is nearly just like super(), except for special handling
        # of coroutines
    
        _call = _Call((args, kwargs), two=True)
        self.await_count += 1
        self.await_args = _call
        self.await_args_list.append(_call)
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:2326: Exception

The above exception was the direct cause of the following exception:

self = <tests.test_setup_endpoints.TestSetupCallback object at 0x7fcdb5e73a80>
test_client = <starlette.testclient.TestClient object at 0x7fcdb181fce0>

    def test_callback_handles_exchange_error(self, test_client):
        """Test callback handles exchange error gracefully."""
        with patch(
            "stampbot.main.exchange_code_for_credentials",
            new_callable=AsyncMock,
            side_effect=Exception("API error"),
        ):
>           response = test_client.get("/setup/callback?code=bad-code")
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_setup_endpoints.py:213: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:473: in get
    return super().get(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1053: in get
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

request = <starlette.requests.Request object at 0x7fcdb5b00e10>
code = 'bad-code'

    @app.get("/setup/callback")
    async def setup_callback(request: Request, code: str) -> Response:
        """Handle callback from GitHub after app creation.
    
        Args:
            request: FastAPI request.
            code: Temporary code from GitHub to exchange for credentials.
    
        Returns:
            HTML page with credentials and setup instructions.
    
        Raises:
            HTTPException: If setup disabled (403) or code exchange fails (500).
        """
        if not settings.setup_enabled:
            raise HTTPException(status_code=403, detail="Setup not allowed")
    
        try:
            credentials = await exchange_code_for_credentials(code)
>       except CosmicRayTestingException as e:
               ^^^^^^^^^^^^^^^^^^^^^^^^^
E       NameError: name 'CosmicRayTestingException' is not defined

stampbot/main.py:472: NameError
=========================== short test summary info ============================
FAILED tests/test_setup_endpoints.py::TestSetupCallback::test_callback_handles_exchange_error - NameError: name 'CosmicRayTestingException' is not defined
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 140 passed, 3 deselected in 1.39s
operator: core/NumberReplacer, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -47,7 +47,7 @@
 APP_VERSION = "0.1.0"
 
 # Security limits
-MAX_WEBHOOK_BODY_SIZE = 1024 * 1024  # 1MB - GitHub webhooks are typically much smaller
+MAX_WEBHOOK_BODY_SIZE = 1025 * 1024  # 1MB - GitHub webhooks are typically much smaller
 
 
 @asynccontextmanager
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/NumberReplacer, occurrence: 1
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -47,7 +47,7 @@
 APP_VERSION = "0.1.0"
 
 # Security limits
-MAX_WEBHOOK_BODY_SIZE = 1024 * 1024  # 1MB - GitHub webhooks are typically much smaller
+MAX_WEBHOOK_BODY_SIZE = 1023 * 1024  # 1MB - GitHub webhooks are typically much smaller
 
 
 @asynccontextmanager
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/NumberReplacer, occurrence: 2
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -47,7 +47,7 @@
 APP_VERSION = "0.1.0"
 
 # Security limits
-MAX_WEBHOOK_BODY_SIZE = 1024 * 1024  # 1MB - GitHub webhooks are typically much smaller
+MAX_WEBHOOK_BODY_SIZE = 1024 * 1025  # 1MB - GitHub webhooks are typically much smaller
 
 
 @asynccontextmanager
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/NumberReplacer, occurrence: 3
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -47,7 +47,7 @@
 APP_VERSION = "0.1.0"
 
 # Security limits
-MAX_WEBHOOK_BODY_SIZE = 1024 * 1024  # 1MB - GitHub webhooks are typically much smaller
+MAX_WEBHOOK_BODY_SIZE = 1024 * 1023  # 1MB - GitHub webhooks are typically much smaller
 
 
 @asynccontextmanager
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/NumberReplacer, occurrence: 4
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -194,7 +194,7 @@
                 if raw:
                     # X-Forwarded-For may be a comma-separated list; the leftmost
                     # entry is the original client (proxies append to the right).
-                    client_ip = raw.split(",")[0].strip()
+                    client_ip = raw.split(",")[ 1].strip()
 
             if not client_ip and scope.get("client"):
                 client_ip = scope["client"][0]
........................................................................ [ 34%]
...................................F
=================================== FAILURES ===================================
______________ test_logging_middleware_respects_configured_header ______________

    def test_logging_middleware_respects_configured_header():
        """Test that logging_middleware uses the configured client_ip_header setting."""
        from unittest.mock import patch
    
        import structlog.contextvars
    
        from stampbot.main import app
    
        with patch("stampbot.main.settings") as mock_settings:
            mock_settings.get = lambda key, default=None: (
                "X-Real-IP" if key == "client_ip_header" else default
            )
    
            client = TestClient(app)
            captured: dict[str, str | None] = {}
    
            original_bind = structlog.contextvars.bind_contextvars
    
            def capturing_bind(**kw: object) -> None:
                captured.update({k: str(v) for k, v in kw.items()})
                original_bind(**kw)
    
            with patch("stampbot.main.structlog.contextvars.bind_contextvars", capturing_bind):
>               client.get("/health", headers={"X-Real-IP": "198.51.100.7"})

tests/test_main.py:294: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:473: in get
    return super().get(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1053: in get
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.main.LoggingMiddleware object at 0x7f0775f82510>
scope = {'app': <fastapi.applications.FastAPI object at 0x7f07761b6f90>, 'client': ('testclient', 50000), 'extensions': {'http...gzip, deflate'), (b'connection', b'keep-alive'), (b'user-agent', b'testclient'), (b'x-real-ip', b'198.51.100.7')], ...}
receive = <function _TestClientTransport.handle_request.<locals>.receive at 0x7f0775e1dc70>
send = <function ServerErrorMiddleware.__call__.<locals>._send at 0x7f0775e1e770>

    async def __call__(self, scope: Any, receive: Any, send: Any) -> None:
        """Process an ASGI event, binding client IP to the structlog context.
    
        For HTTP scopes the middleware clears any inherited structlog context,
        extracts the real client IP from the configured forwarding header (or
        the direct connection address as a fallback), and binds it so that
        every log record emitted during the request includes ``client_ip``.
    
        Non-HTTP scopes (lifespan, WebSocket) are passed through unchanged.
    
        Args:
            scope: ASGI connection scope.
            receive: ASGI receive callable.
            send: ASGI send callable.
        """
        if scope["type"] == "http":
            structlog.contextvars.clear_contextvars()
    
            client_ip: str | None = None
            client_ip_header: str = settings.get("client_ip_header", "X-Forwarded-For")
    
            if client_ip_header:
                raw = Request(scope).headers.get(client_ip_header)
                if raw:
                    # X-Forwarded-For may be a comma-separated list; the leftmost
                    # entry is the original client (proxies append to the right).
>                   client_ip = raw.split(",")[ 1].strip()
                                ^^^^^^^^^^^^^^^^^^
E                   IndexError: list index out of range

stampbot/main.py:197: IndexError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_logging_middleware_respects_configured_header - IndexError: list index out of range
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 107 passed, 3 deselected in 1.07s
operator: core/NumberReplacer, occurrence: 5
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -194,7 +194,7 @@
                 if raw:
                     # X-Forwarded-For may be a comma-separated list; the leftmost
                     # entry is the original client (proxies append to the right).
-                    client_ip = raw.split(",")[0].strip()
+                    client_ip = raw.split(",")[ -1].strip()
 
             if not client_ip and scope.get("client"):
                 client_ip = scope["client"][0]
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/NumberReplacer, occurrence: 6
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -197,7 +197,7 @@
                     client_ip = raw.split(",")[0].strip()
 
             if not client_ip and scope.get("client"):
-                client_ip = scope["client"][0]
+                client_ip = scope["client"][ 1]
 
             if client_ip:
                 structlog.contextvars.bind_contextvars(client_ip=client_ip)
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/NumberReplacer, occurrence: 7
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -197,7 +197,7 @@
                     client_ip = raw.split(",")[0].strip()
 
             if not client_ip and scope.get("client"):
-                client_ip = scope["client"][0]
+                client_ip = scope["client"][ -1]
 
             if client_ip:
                 structlog.contextvars.bind_contextvars(client_ip=client_ip)
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/NumberReplacer, occurrence: 8
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -216,7 +216,7 @@
         Redirect to /setup if unconfigured, otherwise JSON status response.
     """
     if not is_configured() and settings.setup_enabled:
-        return RedirectResponse(url="/setup", status_code=307)
+        return RedirectResponse(url="/setup", status_code= 308)
 
     return Response(
         content='{"app": "stampbot", "version": "' + APP_VERSION + '", "status": "running"}',
........................................................................ [ 34%]
.............................................................F
=================================== FAILURES ===================================
_ TestSetupEndpointsUnconfigured.test_root_redirects_to_setup_when_unconfigured _

self = <tests.test_setup_endpoints.TestSetupEndpointsUnconfigured object at 0x7fe641be7b10>

    def test_root_redirects_to_setup_when_unconfigured(self):
        """Test root redirects to /setup when not configured."""
        with (
            patch("stampbot.main.is_configured", return_value=False),
            patch("stampbot.main.settings") as mock_settings,
        ):
            mock_settings.setup_enabled = True
            mock_settings.app_name = "stampbot"
            mock_settings.host = "0.0.0.0"
            mock_settings.port = 8000
            mock_settings.log_level = "INFO"
    
            from fastapi.testclient import TestClient
    
            from stampbot.main import app
    
            client = TestClient(app, raise_server_exceptions=False)
            response = client.get("/", follow_redirects=False)
    
>           assert response.status_code == 307
E           assert 308 == 307
E            +  where 308 = <Response [308 Permanent Redirect]>.status_code

tests/test_setup_endpoints.py:56: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:31:41.118325Z [info     ] HTTP Request: GET http://testserver/ "HTTP/1.1 308 Permanent Redirect"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: GET http://testserver/ "HTTP/1.1 308 Permanent Redirect"
=========================== short test summary info ============================
FAILED tests/test_setup_endpoints.py::TestSetupEndpointsUnconfigured::test_root_redirects_to_setup_when_unconfigured - assert 308 == 307
 +  where 308 = <Response [308 Permanent Redirect]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 133 passed, 3 deselected in 0.99s
operator: core/NumberReplacer, occurrence: 9
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -216,7 +216,7 @@
         Redirect to /setup if unconfigured, otherwise JSON status response.
     """
     if not is_configured() and settings.setup_enabled:
-        return RedirectResponse(url="/setup", status_code=307)
+        return RedirectResponse(url="/setup", status_code= 306)
 
     return Response(
         content='{"app": "stampbot", "version": "' + APP_VERSION + '", "status": "running"}',
........................................................................ [ 34%]
.............................................................F
=================================== FAILURES ===================================
_ TestSetupEndpointsUnconfigured.test_root_redirects_to_setup_when_unconfigured _

self = <tests.test_setup_endpoints.TestSetupEndpointsUnconfigured object at 0x7fee725bfb10>

    def test_root_redirects_to_setup_when_unconfigured(self):
        """Test root redirects to /setup when not configured."""
        with (
            patch("stampbot.main.is_configured", return_value=False),
            patch("stampbot.main.settings") as mock_settings,
        ):
            mock_settings.setup_enabled = True
            mock_settings.app_name = "stampbot"
            mock_settings.host = "0.0.0.0"
            mock_settings.port = 8000
            mock_settings.log_level = "INFO"
    
            from fastapi.testclient import TestClient
    
            from stampbot.main import app
    
            client = TestClient(app, raise_server_exceptions=False)
            response = client.get("/", follow_redirects=False)
    
>           assert response.status_code == 307
E           assert 306 == 307
E            +  where 306 = <Response [306 ]>.status_code

tests/test_setup_endpoints.py:56: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:36:08.160619Z [info     ] HTTP Request: GET http://testserver/ "HTTP/1.1 306 "
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: GET http://testserver/ "HTTP/1.1 306 "
=========================== short test summary info ============================
FAILED tests/test_setup_endpoints.py::TestSetupEndpointsUnconfigured::test_root_redirects_to_setup_when_unconfigured - assert 306 == 307
 +  where 306 = <Response [306 ]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 133 passed, 3 deselected in 0.99s
operator: core/NumberReplacer, occurrence: 10
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -270,7 +270,7 @@
     # Check if app is configured
     if not is_configured():
         raise HTTPException(
-            status_code=503,
+            status_code= 504,
             detail="Stampbot not configured. Visit /setup to complete setup.",
         )
 
........................................................................ [ 34%]
.................................................................F
=================================== FAILURES ===================================
__ TestSetupEndpointsUnconfigured.test_webhook_returns_503_when_unconfigured ___

self = <tests.test_setup_endpoints.TestSetupEndpointsUnconfigured object at 0x7f0b145e28d0>

    def test_webhook_returns_503_when_unconfigured(self):
        """Test webhook returns 503 when not configured."""
        with (
            patch("stampbot.main.is_configured", return_value=False),
            patch("stampbot.main.settings") as mock_settings,
        ):
            mock_settings.setup_enabled = True
    
            from fastapi.testclient import TestClient
    
            from stampbot.main import app
    
            client = TestClient(app, raise_server_exceptions=False)
            response = client.post("/webhook", json={})
    
>           assert response.status_code == 503
E           assert 504 == 503
E            +  where 504 = <Response [504 Gateway Timeout]>.status_code

tests/test_setup_endpoints.py:155: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:44:55.164043Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 504 Gateway Timeout"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 504 Gateway Timeout"
=========================== short test summary info ============================
FAILED tests/test_setup_endpoints.py::TestSetupEndpointsUnconfigured::test_webhook_returns_503_when_unconfigured - assert 504 == 503
 +  where 504 = <Response [504 Gateway Timeout]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 137 passed, 3 deselected in 1.03s
operator: core/NumberReplacer, occurrence: 11
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -270,7 +270,7 @@
     # Check if app is configured
     if not is_configured():
         raise HTTPException(
-            status_code=503,
+            status_code= 502,
             detail="Stampbot not configured. Visit /setup to complete setup.",
         )
 
........................................................................ [ 34%]
.................................................................F
=================================== FAILURES ===================================
__ TestSetupEndpointsUnconfigured.test_webhook_returns_503_when_unconfigured ___

self = <tests.test_setup_endpoints.TestSetupEndpointsUnconfigured object at 0x7f0cd31468d0>

    def test_webhook_returns_503_when_unconfigured(self):
        """Test webhook returns 503 when not configured."""
        with (
            patch("stampbot.main.is_configured", return_value=False),
            patch("stampbot.main.settings") as mock_settings,
        ):
            mock_settings.setup_enabled = True
    
            from fastapi.testclient import TestClient
    
            from stampbot.main import app
    
            client = TestClient(app, raise_server_exceptions=False)
            response = client.post("/webhook", json={})
    
>           assert response.status_code == 503
E           assert 502 == 503
E            +  where 502 = <Response [502 Bad Gateway]>.status_code

tests/test_setup_endpoints.py:155: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:54:57.118128Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 502 Bad Gateway"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 502 Bad Gateway"
=========================== short test summary info ============================
FAILED tests/test_setup_endpoints.py::TestSetupEndpointsUnconfigured::test_webhook_returns_503_when_unconfigured - assert 502 == 503
 +  where 502 = <Response [502 Bad Gateway]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 137 passed, 3 deselected in 1.03s
operator: core/NumberReplacer, occurrence: 12
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -276,7 +276,7 @@
 
     if not x_github_event:
         errors_total.labels(error_type="missing_event").inc()
-        raise HTTPException(status_code=400, detail="Missing X-GitHub-Event header")
+        raise HTTPException(status_code= 401, detail="Missing X-GitHub-Event header")
 
     # Check content length before reading body to prevent memory exhaustion
     content_length = request.headers.get("content-length")
........................................................................ [ 34%]
..........................F
=================================== FAILURES ===================================
______________________ test_webhook_missing_event_header _______________________

test_client = <starlette.testclient.TestClient object at 0x7f00fa3d4f30>

    def test_webhook_missing_event_header(test_client: TestClient):
        """Test webhook rejects requests without X-GitHub-Event header."""
        response = test_client.post(
            "/webhook",
            json={"zen": "test"},
            headers={"X-Hub-Signature-256": "sha256=invalid"},
        )
>       assert response.status_code == 400
E       assert 401 == 400
E        +  where 401 = <Response [401 Unauthorized]>.status_code

tests/test_main.py:100: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:37:16.782360Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 401 Unauthorized"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 401 Unauthorized"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_missing_event_header - assert 401 == 400
 +  where 401 = <Response [401 Unauthorized]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 98 passed, 3 deselected in 0.85s
operator: core/NumberReplacer, occurrence: 13
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -276,7 +276,7 @@
 
     if not x_github_event:
         errors_total.labels(error_type="missing_event").inc()
-        raise HTTPException(status_code=400, detail="Missing X-GitHub-Event header")
+        raise HTTPException(status_code= 399, detail="Missing X-GitHub-Event header")
 
     # Check content length before reading body to prevent memory exhaustion
     content_length = request.headers.get("content-length")
........................................................................ [ 34%]
..........................F
=================================== FAILURES ===================================
______________________ test_webhook_missing_event_header _______________________

test_client = <starlette.testclient.TestClient object at 0x7f31633d8f30>

    def test_webhook_missing_event_header(test_client: TestClient):
        """Test webhook rejects requests without X-GitHub-Event header."""
        response = test_client.post(
            "/webhook",
            json={"zen": "test"},
            headers={"X-Hub-Signature-256": "sha256=invalid"},
        )
>       assert response.status_code == 400
E       assert 399 == 400
E        +  where 399 = <Response [399 ]>.status_code

tests/test_main.py:100: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:31:00.328876Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 399 "
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 399 "
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_missing_event_header - assert 399 == 400
 +  where 399 = <Response [399 ]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 98 passed, 3 deselected in 0.85s
operator: core/NumberReplacer, occurrence: 14
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -282,7 +282,7 @@
     content_length = request.headers.get("content-length")
     if content_length and int(content_length) > MAX_WEBHOOK_BODY_SIZE:
         errors_total.labels(error_type="payload_too_large").inc()
-        raise HTTPException(status_code=413, detail="Request body too large")
+        raise HTTPException(status_code= 414, detail="Request body too large")
 
     # Get raw body for signature verification
     body = await request.body()
........................................................................ [ 34%]
..............................F
=================================== FAILURES ===================================
____________________ test_webhook_content_length_too_large _____________________

test_client = <starlette.testclient.TestClient object at 0x7fa2a2fc6f10>

    def test_webhook_content_length_too_large(test_client: TestClient):
        """Test webhook rejects requests with Content-Length exceeding limit."""
        response = test_client.post(
            "/webhook",
            content=b"x",  # Small actual body
            headers={
                "Content-Type": "application/json",
                "Content-Length": "20000000",  # 20MB - exceeds 10MB limit
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=fake",
            },
        )
>       assert response.status_code == 413
E       assert 414 == 413
E        +  where 414 = <Response [414 Request-URI Too Long]>.status_code

tests/test_main.py:193: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:26:05.116811Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 414 Request-URI Too Long"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 414 Request-URI Too Long"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_content_length_too_large - assert 414 == 413
 +  where 414 = <Response [414 Request-URI Too Long]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 102 passed, 3 deselected in 0.84s
operator: core/NumberReplacer, occurrence: 15
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -282,7 +282,7 @@
     content_length = request.headers.get("content-length")
     if content_length and int(content_length) > MAX_WEBHOOK_BODY_SIZE:
         errors_total.labels(error_type="payload_too_large").inc()
-        raise HTTPException(status_code=413, detail="Request body too large")
+        raise HTTPException(status_code= 412, detail="Request body too large")
 
     # Get raw body for signature verification
     body = await request.body()
........................................................................ [ 34%]
..............................F
=================================== FAILURES ===================================
____________________ test_webhook_content_length_too_large _____________________

test_client = <starlette.testclient.TestClient object at 0x7f1ddf3c6f10>

    def test_webhook_content_length_too_large(test_client: TestClient):
        """Test webhook rejects requests with Content-Length exceeding limit."""
        response = test_client.post(
            "/webhook",
            content=b"x",  # Small actual body
            headers={
                "Content-Type": "application/json",
                "Content-Length": "20000000",  # 20MB - exceeds 10MB limit
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=fake",
            },
        )
>       assert response.status_code == 413
E       assert 412 == 413
E        +  where 412 = <Response [412 Precondition Failed]>.status_code

tests/test_main.py:193: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:26:55.929965Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 412 Precondition Failed"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 412 Precondition Failed"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_content_length_too_large - assert 412 == 413
 +  where 412 = <Response [412 Precondition Failed]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 102 passed, 3 deselected in 0.87s
operator: core/NumberReplacer, occurrence: 16
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -290,7 +290,7 @@
     # Double-check actual body size (in case content-length was missing/incorrect)
     if len(body) > MAX_WEBHOOK_BODY_SIZE:
         errors_total.labels(error_type="payload_too_large").inc()
-        raise HTTPException(status_code=413, detail="Request body too large")
+        raise HTTPException(status_code= 414, detail="Request body too large")
 
     # Verify signature
     if not webhook_handler.verify_signature(body, x_hub_signature_256):
........................................................................ [ 34%]
...............................F
=================================== FAILURES ===================================
_________________________ test_webhook_body_too_large __________________________

test_client = <starlette.testclient.TestClient object at 0x7f488e2eb790>

    def test_webhook_body_too_large(test_client: TestClient):
        """Test webhook rejects requests where actual body exceeds limit.
    
        This tests the secondary body size check (lines 231-232) which catches
        cases where Content-Length header is missing or incorrect.
        """
        # Create a body larger than MAX_WEBHOOK_BODY_SIZE (10MB)
        large_body = b"x" * (10 * 1024 * 1024 + 1)  # 10MB + 1 byte
    
        # Set Content-Length to a small value to bypass the header check (line 222)
        # but the actual body size check (line 230) should still catch it
        response = test_client.post(
            "/webhook",
            content=large_body,
            headers={
                "Content-Type": "application/json",
                "Content-Length": "100",  # Lie about size to bypass first check
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=fake",
            },
        )
>       assert response.status_code == 413
E       assert 414 == 413
E        +  where 414 = <Response [414 Request-URI Too Long]>.status_code

tests/test_main.py:218: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:41:34.939638Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 414 Request-URI Too Long"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 414 Request-URI Too Long"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_body_too_large - assert 414 == 413
 +  where 414 = <Response [414 Request-URI Too Long]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 103 passed, 3 deselected in 0.87s
operator: core/NumberReplacer, occurrence: 17
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -290,7 +290,7 @@
     # Double-check actual body size (in case content-length was missing/incorrect)
     if len(body) > MAX_WEBHOOK_BODY_SIZE:
         errors_total.labels(error_type="payload_too_large").inc()
-        raise HTTPException(status_code=413, detail="Request body too large")
+        raise HTTPException(status_code= 412, detail="Request body too large")
 
     # Verify signature
     if not webhook_handler.verify_signature(body, x_hub_signature_256):
........................................................................ [ 34%]
...............................F
=================================== FAILURES ===================================
_________________________ test_webhook_body_too_large __________________________

test_client = <starlette.testclient.TestClient object at 0x7f09e2daf790>

    def test_webhook_body_too_large(test_client: TestClient):
        """Test webhook rejects requests where actual body exceeds limit.
    
        This tests the secondary body size check (lines 231-232) which catches
        cases where Content-Length header is missing or incorrect.
        """
        # Create a body larger than MAX_WEBHOOK_BODY_SIZE (10MB)
        large_body = b"x" * (10 * 1024 * 1024 + 1)  # 10MB + 1 byte
    
        # Set Content-Length to a small value to bypass the header check (line 222)
        # but the actual body size check (line 230) should still catch it
        response = test_client.post(
            "/webhook",
            content=large_body,
            headers={
                "Content-Type": "application/json",
                "Content-Length": "100",  # Lie about size to bypass first check
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=fake",
            },
        )
>       assert response.status_code == 413
E       assert 412 == 413
E        +  where 412 = <Response [412 Precondition Failed]>.status_code

tests/test_main.py:218: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:07:48.123699Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 412 Precondition Failed"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 412 Precondition Failed"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_body_too_large - assert 412 == 413
 +  where 412 = <Response [412 Precondition Failed]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 103 passed, 3 deselected in 0.88s
operator: core/NumberReplacer, occurrence: 18
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -297,7 +297,7 @@
         webhook_signature_validations_total.labels(result="invalid").inc()
         errors_total.labels(error_type="signature_invalid").inc()
         logger.warning("Invalid webhook signature")
-        raise HTTPException(status_code=401, detail="Invalid signature")
+        raise HTTPException(status_code= 402, detail="Invalid signature")
 
     webhook_signature_validations_total.labels(result="valid").inc()
 
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7f33f5fc7770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
        response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )
        # Will fail signature validation, but Content-Length tracking happens before that
>       assert response.status_code == 401
E       assert 402 == 401
E        +  where 402 = <Response [402 Payment Required]>.status_code

tests/test_main.py:79: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:29:38.037412Z [warning  ] Invalid webhook signature      _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=warning client_ip=testclient
2026-03-27T13:29:38.038332Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 402 Payment Required"
------------------------------ Captured log call -------------------------------
WARNING  stampbot.main:main.py:299 {'event': 'Invalid webhook signature', 'client_ip': 'testclient', 'level': 'warning', 'timestamp': '2026-03-27T13:29:38.037412Z'}
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 402 Payment Required"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - assert 402 == 401
 +  where 402 = <Response [402 Payment Required]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 0.84s
operator: core/NumberReplacer, occurrence: 19
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -297,7 +297,7 @@
         webhook_signature_validations_total.labels(result="invalid").inc()
         errors_total.labels(error_type="signature_invalid").inc()
         logger.warning("Invalid webhook signature")
-        raise HTTPException(status_code=401, detail="Invalid signature")
+        raise HTTPException(status_code= 400, detail="Invalid signature")
 
     webhook_signature_validations_total.labels(result="valid").inc()
 
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7fe3dc9ab770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
        response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )
        # Will fail signature validation, but Content-Length tracking happens before that
>       assert response.status_code == 401
E       assert 400 == 401
E        +  where 400 = <Response [400 Bad Request]>.status_code

tests/test_main.py:79: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:10:14.345583Z [warning  ] Invalid webhook signature      _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=warning client_ip=testclient
2026-03-27T14:10:14.346485Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 400 Bad Request"
------------------------------ Captured log call -------------------------------
WARNING  stampbot.main:main.py:299 {'event': 'Invalid webhook signature', 'client_ip': 'testclient', 'level': 'warning', 'timestamp': '2026-03-27T14:10:14.345583Z'}
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 400 Bad Request"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - assert 400 == 401
 +  where 400 = <Response [400 Bad Request]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 0.85s
operator: core/NumberReplacer, occurrence: 20
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -307,7 +307,7 @@
     except Exception as e:
         errors_total.labels(error_type="payload_invalid").inc()
         logger.error("Failed to parse webhook payload: %s", e)
-        raise HTTPException(status_code=400, detail="Invalid JSON payload") from None
+        raise HTTPException(status_code= 401, detail="Invalid JSON payload") from None
 
     # Handle event with timing
     try:
........................................................................ [ 34%]
............................F
=================================== FAILURES ===================================
__________________________ test_webhook_invalid_json ___________________________

test_client = <starlette.testclient.TestClient object at 0x7fce77db6580>

    def test_webhook_invalid_json(test_client: TestClient):
        """Test webhook rejects invalid JSON payload."""
        import hashlib
        import hmac
    
        from stampbot.webhook_handler import webhook_handler
    
        # Create a valid signature for invalid JSON
        body = b"not valid json"
        signature = (
            "sha256="
            + hmac.new(
                webhook_handler.webhook_secret,
                body,
                hashlib.sha256,
            ).hexdigest()
        )
    
        response = test_client.post(
            "/webhook",
            content=body,
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": signature,
            },
        )
>       assert response.status_code == 400
E       assert 401 == 400
E        +  where 401 = <Response [401 Unauthorized]>.status_code

tests/test_main.py:145: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:42:21.914093Z [error    ] Failed to parse webhook payload: Expecting value: line 1 column 1 (char 0) _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=error client_ip=testclient
2026-03-27T13:42:21.914924Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 401 Unauthorized"
------------------------------ Captured log call -------------------------------
ERROR    stampbot.main:main.py:309 {'event': 'Failed to parse webhook payload: Expecting value: line 1 column 1 (char 0)', 'client_ip': 'testclient', 'level': 'error', 'timestamp': '2026-03-27T13:42:21.914093Z'}
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 401 Unauthorized"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_invalid_json - assert 401 == 400
 +  where 401 = <Response [401 Unauthorized]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 100 passed, 3 deselected in 0.85s
operator: core/NumberReplacer, occurrence: 21
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -307,7 +307,7 @@
     except Exception as e:
         errors_total.labels(error_type="payload_invalid").inc()
         logger.error("Failed to parse webhook payload: %s", e)
-        raise HTTPException(status_code=400, detail="Invalid JSON payload") from None
+        raise HTTPException(status_code= 399, detail="Invalid JSON payload") from None
 
     # Handle event with timing
     try:
........................................................................ [ 34%]
............................F
=================================== FAILURES ===================================
__________________________ test_webhook_invalid_json ___________________________

test_client = <starlette.testclient.TestClient object at 0x7f4ffb0f2580>

    def test_webhook_invalid_json(test_client: TestClient):
        """Test webhook rejects invalid JSON payload."""
        import hashlib
        import hmac
    
        from stampbot.webhook_handler import webhook_handler
    
        # Create a valid signature for invalid JSON
        body = b"not valid json"
        signature = (
            "sha256="
            + hmac.new(
                webhook_handler.webhook_secret,
                body,
                hashlib.sha256,
            ).hexdigest()
        )
    
        response = test_client.post(
            "/webhook",
            content=body,
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": signature,
            },
        )
>       assert response.status_code == 400
E       assert 399 == 400
E        +  where 399 = <Response [399 ]>.status_code

tests/test_main.py:145: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:43:20.791517Z [error    ] Failed to parse webhook payload: Expecting value: line 1 column 1 (char 0) _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=error client_ip=testclient
2026-03-27T13:43:20.792410Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 399 "
------------------------------ Captured log call -------------------------------
ERROR    stampbot.main:main.py:309 {'event': 'Failed to parse webhook payload: Expecting value: line 1 column 1 (char 0)', 'client_ip': 'testclient', 'level': 'error', 'timestamp': '2026-03-27T13:43:20.791517Z'}
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 399 "
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_invalid_json - assert 399 == 400
 +  where 399 = <Response [399 ]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 100 passed, 3 deselected in 0.85s
operator: core/NumberReplacer, occurrence: 22
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -324,7 +324,7 @@
         errors_total.labels(error_type="webhook_handler_error").inc()
         sanitized = _sanitize_error(e)
         logger.error("Error handling webhook event: %s", sanitized, extra={"error": sanitized})
-        raise HTTPException(status_code=500, detail="Internal server error") from None
+        raise HTTPException(status_code= 501, detail="Internal server error") from None
 
 
 # =============================================================================
........................................................................ [ 34%]
.....................................F
=================================== FAILURES ===================================
________________________ test_webhook_handler_exception ________________________

test_client = <starlette.testclient.TestClient object at 0x7fefd98d3f00>

    def test_webhook_handler_exception(test_client: TestClient):
        """Test webhook returns 500 when handler raises exception."""
        from stampbot.webhook_handler import webhook_handler
    
        body = json.dumps({"zen": "test"}).encode()
        signature = (
            "sha256="
            + hmac.new(
                webhook_handler.webhook_secret,
                body,
                hashlib.sha256,
            ).hexdigest()
        )
    
        with patch("stampbot.main.webhook_handler.handle_event") as mock_handle:
            mock_handle.side_effect = Exception("Unexpected error")
    
            response = test_client.post(
                "/webhook",
                content=body,
                headers={
                    "Content-Type": "application/json",
                    "X-GitHub-Event": "ping",
                    "X-Hub-Signature-256": signature,
                },
            )
    
>       assert response.status_code == 500
E       assert 501 == 500
E        +  where 501 = <Response [501 Not Implemented]>.status_code

tests/test_main.py:360: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:53:47.010155Z [error    ] Error handling webhook event: Unexpected error _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=error client_ip=testclient extra={'error': 'Unexpected error'}
2026-03-27T13:53:47.010972Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 501 Not Implemented"
------------------------------ Captured log call -------------------------------
ERROR    stampbot.main:main.py:326 {'extra': {'error': 'Unexpected error'}, 'event': 'Error handling webhook event: Unexpected error', 'client_ip': 'testclient', 'level': 'error', 'timestamp': '2026-03-27T13:53:47.010155Z'}
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 501 Not Implemented"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_handler_exception - assert 501 == 500
 +  where 501 = <Response [501 Not Implemented]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 109 passed, 3 deselected in 0.88s
operator: core/NumberReplacer, occurrence: 23
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -324,7 +324,7 @@
         errors_total.labels(error_type="webhook_handler_error").inc()
         sanitized = _sanitize_error(e)
         logger.error("Error handling webhook event: %s", sanitized, extra={"error": sanitized})
-        raise HTTPException(status_code=500, detail="Internal server error") from None
+        raise HTTPException(status_code= 499, detail="Internal server error") from None
 
 
 # =============================================================================
........................................................................ [ 34%]
.....................................F
=================================== FAILURES ===================================
________________________ test_webhook_handler_exception ________________________

test_client = <starlette.testclient.TestClient object at 0x7f2b8dfbff00>

    def test_webhook_handler_exception(test_client: TestClient):
        """Test webhook returns 500 when handler raises exception."""
        from stampbot.webhook_handler import webhook_handler
    
        body = json.dumps({"zen": "test"}).encode()
        signature = (
            "sha256="
            + hmac.new(
                webhook_handler.webhook_secret,
                body,
                hashlib.sha256,
            ).hexdigest()
        )
    
        with patch("stampbot.main.webhook_handler.handle_event") as mock_handle:
            mock_handle.side_effect = Exception("Unexpected error")
    
            response = test_client.post(
                "/webhook",
                content=body,
                headers={
                    "Content-Type": "application/json",
                    "X-GitHub-Event": "ping",
                    "X-Hub-Signature-256": signature,
                },
            )
    
>       assert response.status_code == 500
E       assert 499 == 500
E        +  where 499 = <Response [499 ]>.status_code

tests/test_main.py:360: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:07:31.743650Z [error    ] Error handling webhook event: Unexpected error _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=error client_ip=testclient extra={'error': 'Unexpected error'}
2026-03-27T14:07:31.744480Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 499 "
------------------------------ Captured log call -------------------------------
ERROR    stampbot.main:main.py:326 {'extra': {'error': 'Unexpected error'}, 'event': 'Error handling webhook event: Unexpected error', 'client_ip': 'testclient', 'level': 'error', 'timestamp': '2026-03-27T14:07:31.743650Z'}
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 499 "
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_handler_exception - assert 499 == 500
 +  where 499 = <Response [499 ]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 109 passed, 3 deselected in 0.88s
operator: core/NumberReplacer, occurrence: 24
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -346,7 +346,7 @@
         HTTPException: If setup is disabled (403).
     """
     if not settings.setup_enabled:
-        raise HTTPException(status_code=403, detail="Setup not allowed in this environment")
+        raise HTTPException(status_code= 404, detail="Setup not allowed in this environment")
 
     if is_configured():
         return HTMLResponse(
........................................................................ [ 34%]
.....................................................................F
=================================== FAILURES ===================================
____________ TestSetupDisabled.test_setup_returns_403_when_disabled ____________

self = <tests.test_setup_endpoints.TestSetupDisabled object at 0x7f6183744050>

    def test_setup_returns_403_when_disabled(self):
        """Test /setup returns 403 when setup is disabled."""
        with (
            patch("stampbot.main.is_configured", return_value=False),
            patch("stampbot.main.settings") as mock_settings,
        ):
            mock_settings.setup_enabled = False
    
            from fastapi.testclient import TestClient
    
            from stampbot.main import app
    
            client = TestClient(app, raise_server_exceptions=False)
            response = client.get("/setup")
    
>           assert response.status_code == 403
E           assert 404 == 403
E            +  where 404 = <Response [404 Not Found]>.status_code

tests/test_setup_endpoints.py:236: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:57:35.937845Z [info     ] HTTP Request: GET http://testserver/setup "HTTP/1.1 404 Not Found"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: GET http://testserver/setup "HTTP/1.1 404 Not Found"
=========================== short test summary info ============================
FAILED tests/test_setup_endpoints.py::TestSetupDisabled::test_setup_returns_403_when_disabled - assert 404 == 403
 +  where 404 = <Response [404 Not Found]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 141 passed, 3 deselected in 1.05s
operator: core/NumberReplacer, occurrence: 25
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -346,7 +346,7 @@
         HTTPException: If setup is disabled (403).
     """
     if not settings.setup_enabled:
-        raise HTTPException(status_code=403, detail="Setup not allowed in this environment")
+        raise HTTPException(status_code= 402, detail="Setup not allowed in this environment")
 
     if is_configured():
         return HTMLResponse(
........................................................................ [ 34%]
.....................................................................F
=================================== FAILURES ===================================
____________ TestSetupDisabled.test_setup_returns_403_when_disabled ____________

self = <tests.test_setup_endpoints.TestSetupDisabled object at 0x7feff3fec050>

    def test_setup_returns_403_when_disabled(self):
        """Test /setup returns 403 when setup is disabled."""
        with (
            patch("stampbot.main.is_configured", return_value=False),
            patch("stampbot.main.settings") as mock_settings,
        ):
            mock_settings.setup_enabled = False
    
            from fastapi.testclient import TestClient
    
            from stampbot.main import app
    
            client = TestClient(app, raise_server_exceptions=False)
            response = client.get("/setup")
    
>           assert response.status_code == 403
E           assert 402 == 403
E            +  where 402 = <Response [402 Payment Required]>.status_code

tests/test_setup_endpoints.py:236: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:08:15.677166Z [info     ] HTTP Request: GET http://testserver/setup "HTTP/1.1 402 Payment Required"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: GET http://testserver/setup "HTTP/1.1 402 Payment Required"
=========================== short test summary info ============================
FAILED tests/test_setup_endpoints.py::TestSetupDisabled::test_setup_returns_403_when_disabled - assert 402 == 403
 +  where 402 = <Response [402 Payment Required]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 141 passed, 3 deselected in 1.05s
operator: core/NumberReplacer, occurrence: 26
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -371,7 +371,7 @@
             </body>
             </html>
             """,
-            status_code=200,
+            status_code= 201,
         )
 
     # Determine base URL with priority:
........................................................................ [ 34%]
...........................................................F
=================================== FAILURES ===================================
_______ TestSetupEndpointsConfigured.test_setup_shows_already_configured _______

self = <tests.test_setup_endpoints.TestSetupEndpointsConfigured object at 0x7f639fce79d0>
test_client = <starlette.testclient.TestClient object at 0x7f639aa2ead0>

    def test_setup_shows_already_configured(self, test_client):
        """Test /setup shows already configured message."""
        response = test_client.get("/setup")
    
>       assert response.status_code == 200
E       assert 201 == 200
E        +  where 201 = <Response [201 Created]>.status_code

tests/test_setup_endpoints.py:22: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:38:41.253994Z [info     ] HTTP Request: GET http://testserver/setup "HTTP/1.1 201 Created"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: GET http://testserver/setup "HTTP/1.1 201 Created"
=========================== short test summary info ============================
FAILED tests/test_setup_endpoints.py::TestSetupEndpointsConfigured::test_setup_shows_already_configured - assert 201 == 200
 +  where 201 = <Response [201 Created]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 131 passed, 3 deselected in 0.99s
operator: core/NumberReplacer, occurrence: 27
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -371,7 +371,7 @@
             </body>
             </html>
             """,
-            status_code=200,
+            status_code= 199,
         )
 
     # Determine base URL with priority:
........................................................................ [ 34%]
...........................................................F
=================================== FAILURES ===================================
_______ TestSetupEndpointsConfigured.test_setup_shows_already_configured _______

self = <tests.test_setup_endpoints.TestSetupEndpointsConfigured object at 0x7fa96777f9d0>
test_client = <starlette.testclient.TestClient object at 0x7fa962512ad0>

    def test_setup_shows_already_configured(self, test_client):
        """Test /setup shows already configured message."""
        response = test_client.get("/setup")
    
>       assert response.status_code == 200
E       assert 199 == 200
E        +  where 199 = <Response [199 ]>.status_code

tests/test_setup_endpoints.py:22: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:31:48.104074Z [info     ] HTTP Request: GET http://testserver/setup "HTTP/1.1 199 "
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: GET http://testserver/setup "HTTP/1.1 199 "
=========================== short test summary info ============================
FAILED tests/test_setup_endpoints.py::TestSetupEndpointsConfigured::test_setup_shows_already_configured - assert 199 == 200
 +  where 199 = <Response [199 ]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 131 passed, 3 deselected in 0.98s
operator: core/NumberReplacer, occurrence: 28
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -465,7 +465,7 @@
         HTTPException: If setup disabled (403) or code exchange fails (500).
     """
     if not settings.setup_enabled:
-        raise HTTPException(status_code=403, detail="Setup not allowed")
+        raise HTTPException(status_code= 404, detail="Setup not allowed")
 
     try:
         credentials = await exchange_code_for_credentials(code)
........................................................................ [ 34%]
......................................................................F
=================================== FAILURES ===================================
_______ TestSetupDisabled.test_setup_callback_returns_403_when_disabled ________

self = <tests.test_setup_endpoints.TestSetupDisabled object at 0x7faab8ff0190>

    def test_setup_callback_returns_403_when_disabled(self):
        """Test /setup/callback returns 403 when setup is disabled."""
        with patch("stampbot.main.settings") as mock_settings:
            mock_settings.setup_enabled = False
    
            from fastapi.testclient import TestClient
    
            from stampbot.main import app
    
            client = TestClient(app, raise_server_exceptions=False)
            response = client.get("/setup/callback?code=test")
    
>           assert response.status_code == 403
E           assert 404 == 403
E            +  where 404 = <Response [404 Not Found]>.status_code

tests/test_setup_endpoints.py:250: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:06:11.204266Z [info     ] HTTP Request: GET http://testserver/setup/callback?code=test "HTTP/1.1 404 Not Found"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: GET http://testserver/setup/callback?code=test "HTTP/1.1 404 Not Found"
=========================== short test summary info ============================
FAILED tests/test_setup_endpoints.py::TestSetupDisabled::test_setup_callback_returns_403_when_disabled - assert 404 == 403
 +  where 404 = <Response [404 Not Found]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 142 passed, 3 deselected in 1.04s
operator: core/NumberReplacer, occurrence: 29
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -465,7 +465,7 @@
         HTTPException: If setup disabled (403) or code exchange fails (500).
     """
     if not settings.setup_enabled:
-        raise HTTPException(status_code=403, detail="Setup not allowed")
+        raise HTTPException(status_code= 402, detail="Setup not allowed")
 
     try:
         credentials = await exchange_code_for_credentials(code)
........................................................................ [ 34%]
......................................................................F
=================================== FAILURES ===================================
_______ TestSetupDisabled.test_setup_callback_returns_403_when_disabled ________

self = <tests.test_setup_endpoints.TestSetupDisabled object at 0x7f839f348190>

    def test_setup_callback_returns_403_when_disabled(self):
        """Test /setup/callback returns 403 when setup is disabled."""
        with patch("stampbot.main.settings") as mock_settings:
            mock_settings.setup_enabled = False
    
            from fastapi.testclient import TestClient
    
            from stampbot.main import app
    
            client = TestClient(app, raise_server_exceptions=False)
            response = client.get("/setup/callback?code=test")
    
>           assert response.status_code == 403
E           assert 402 == 403
E            +  where 402 = <Response [402 Payment Required]>.status_code

tests/test_setup_endpoints.py:250: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:26:59.946464Z [info     ] HTTP Request: GET http://testserver/setup/callback?code=test "HTTP/1.1 402 Payment Required"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: GET http://testserver/setup/callback?code=test "HTTP/1.1 402 Payment Required"
=========================== short test summary info ============================
FAILED tests/test_setup_endpoints.py::TestSetupDisabled::test_setup_callback_returns_403_when_disabled - assert 402 == 403
 +  where 402 = <Response [402 Payment Required]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 142 passed, 3 deselected in 1.04s
operator: core/NumberReplacer, occurrence: 30
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -471,7 +471,7 @@
         credentials = await exchange_code_for_credentials(code)
     except Exception as e:
         logger.error("Failed to exchange code for credentials: %s", _sanitize_error(e))
-        raise HTTPException(status_code=500, detail="Failed to complete setup") from None
+        raise HTTPException(status_code= 501, detail="Failed to complete setup") from None
 
     # Security note: Credentials are displayed in the HTML response for user convenience.
     # This mirrors GitHub's own manifest flow behavior. The tradeoffs are acceptable because:
........................................................................ [ 34%]
....................................................................F
=================================== FAILURES ===================================
____________ TestSetupCallback.test_callback_handles_exchange_error ____________

self = <tests.test_setup_endpoints.TestSetupCallback object at 0x7fe37f23ba80>
test_client = <starlette.testclient.TestClient object at 0x7fe37ac03ce0>

    def test_callback_handles_exchange_error(self, test_client):
        """Test callback handles exchange error gracefully."""
        with patch(
            "stampbot.main.exchange_code_for_credentials",
            new_callable=AsyncMock,
            side_effect=Exception("API error"),
        ):
            response = test_client.get("/setup/callback?code=bad-code")
    
>           assert response.status_code == 500
E           assert 501 == 500
E            +  where 501 = <Response [501 Not Implemented]>.status_code

tests/test_setup_endpoints.py:215: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:09:47.077193Z [error    ] Failed to exchange code for credentials: API error _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=error client_ip=testclient
2026-03-27T14:09:47.078056Z [info     ] HTTP Request: GET http://testserver/setup/callback?code=bad-code "HTTP/1.1 501 Not Implemented"
------------------------------ Captured log call -------------------------------
ERROR    stampbot.main:main.py:473 {'event': 'Failed to exchange code for credentials: API error', 'client_ip': 'testclient', 'level': 'error', 'timestamp': '2026-03-27T14:09:47.077193Z'}
INFO     httpx:_client.py:1025 HTTP Request: GET http://testserver/setup/callback?code=bad-code "HTTP/1.1 501 Not Implemented"
=========================== short test summary info ============================
FAILED tests/test_setup_endpoints.py::TestSetupCallback::test_callback_handles_exchange_error - assert 501 == 500
 +  where 501 = <Response [501 Not Implemented]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 140 passed, 3 deselected in 1.03s
operator: core/NumberReplacer, occurrence: 31
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -471,7 +471,7 @@
         credentials = await exchange_code_for_credentials(code)
     except Exception as e:
         logger.error("Failed to exchange code for credentials: %s", _sanitize_error(e))
-        raise HTTPException(status_code=500, detail="Failed to complete setup") from None
+        raise HTTPException(status_code= 499, detail="Failed to complete setup") from None
 
     # Security note: Credentials are displayed in the HTML response for user convenience.
     # This mirrors GitHub's own manifest flow behavior. The tradeoffs are acceptable because:
........................................................................ [ 34%]
....................................................................F
=================================== FAILURES ===================================
____________ TestSetupCallback.test_callback_handles_exchange_error ____________

self = <tests.test_setup_endpoints.TestSetupCallback object at 0x7fceb7347a80>
test_client = <starlette.testclient.TestClient object at 0x7fceb2c37ce0>

    def test_callback_handles_exchange_error(self, test_client):
        """Test callback handles exchange error gracefully."""
        with patch(
            "stampbot.main.exchange_code_for_credentials",
            new_callable=AsyncMock,
            side_effect=Exception("API error"),
        ):
            response = test_client.get("/setup/callback?code=bad-code")
    
>           assert response.status_code == 500
E           assert 499 == 500
E            +  where 499 = <Response [499 ]>.status_code

tests/test_setup_endpoints.py:215: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:41:47.006902Z [error    ] Failed to exchange code for credentials: API error _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=error client_ip=testclient
2026-03-27T13:41:47.007755Z [info     ] HTTP Request: GET http://testserver/setup/callback?code=bad-code "HTTP/1.1 499 "
------------------------------ Captured log call -------------------------------
ERROR    stampbot.main:main.py:473 {'event': 'Failed to exchange code for credentials: API error', 'client_ip': 'testclient', 'level': 'error', 'timestamp': '2026-03-27T13:41:47.006902Z'}
INFO     httpx:_client.py:1025 HTTP Request: GET http://testserver/setup/callback?code=bad-code "HTTP/1.1 499 "
=========================== short test summary info ============================
FAILED tests/test_setup_endpoints.py::TestSetupCallback::test_callback_handles_exchange_error - assert 499 == 500
 +  where 499 = <Response [499 ]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 140 passed, 3 deselected in 1.02s
operator: core/RemoveDecorator, occurrence: 0
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -48,9 +48,6 @@
 
 # Security limits
 MAX_WEBHOOK_BODY_SIZE = 1024 * 1024  # 1MB - GitHub webhooks are typically much smaller
-
-
-@asynccontextmanager
 async def lifespan(app: FastAPI) -> AsyncIterator[None]:
     """Application lifespan manager for startup and shutdown events.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
=============================== warnings summary ===============================
tests/test_main.py::test_lifespan_startup_shutdown_configured
  /home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:618: DeprecationWarning: async generator function lifespans are deprecated, use an @contextlib.asynccontextmanager function instead
    warnings.warn(

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
211 passed, 3 deselected, 1 warning in 1.62s
operator: core/RemoveDecorator, occurrence: 1
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -95,9 +95,6 @@
 
 # Set app info metric
 set_app_info(APP_VERSION)
-
-
-@app.middleware("http")
 async def metrics_middleware(request: Request, call_next: Any) -> Response:
     """Middleware to track HTTP metrics.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/RemoveDecorator, occurrence: 2
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -206,9 +206,6 @@
 
 
 app.add_middleware(LoggingMiddleware)
-
-
-@app.get("/")
 async def root() -> Response:
     """Root endpoint - redirects to setup if not configured.
 
........................................................................ [ 34%]
.....................F
=================================== FAILURES ===================================
______________________________ test_root_endpoint ______________________________

test_client = <starlette.testclient.TestClient object at 0x7eff51290050>

    def test_root_endpoint(test_client: TestClient):
        """Test root endpoint returns basic info."""
        response = test_client.get("/")
>       assert response.status_code == 200
E       assert 404 == 200
E        +  where 404 = <Response [404 Not Found]>.status_code

tests/test_main.py:45: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:09:17.066466Z [info     ] HTTP Request: GET http://testserver/ "HTTP/1.1 404 Not Found"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: GET http://testserver/ "HTTP/1.1 404 Not Found"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_root_endpoint - assert 404 == 200
 +  where 404 = <Response [404 Not Found]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 93 passed, 3 deselected in 0.82s
operator: core/RemoveDecorator, occurrence: 3
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -222,9 +222,6 @@
         content='{"app": "stampbot", "version": "' + APP_VERSION + '", "status": "running"}',
         media_type="application/json",
     )
-
-
-@app.get("/health")
 async def health() -> dict[str, str]:
     """Health check endpoint.
 
........................................................................ [ 34%]
...................F
=================================== FAILURES ===================================
__________________ test_lifespan_startup_shutdown_configured ___________________

    def test_lifespan_startup_shutdown_configured():
        """Test app lifespan startup and shutdown when configured."""
        from stampbot.main import app
    
        # Use TestClient as context manager to trigger lifespan events
        with TestClient(app) as client:
            # App should be running
            response = client.get("/health")
>           assert response.status_code == 200
E           assert 404 == 200
E            +  where 404 = <Response [404 Not Found]>.status_code

tests/test_main.py:21: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:11:18.626131Z [info     ] OpenTelemetry disabled         _logger=<_FixedFindCallerLogger stampbot.telemetry (INFO)> _name=info
2026-03-27T14:11:18.644831Z [info     ] Starting stampbot on 0.0.0.0:8000 _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=info extra={'host': '0.0.0.0', 'port': 8000, 'log_level': 'INFO'}
2026-03-27T14:11:18.645127Z [info     ] GitHub App credentials configured successfully _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=info
2026-03-27T14:11:18.647051Z [info     ] HTTP Request: GET http://testserver/health "HTTP/1.1 404 Not Found"
2026-03-27T14:11:18.647459Z [info     ] Shutting down stampbot         _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=info
=========================== short test summary info ============================
FAILED tests/test_main.py::test_lifespan_startup_shutdown_configured - assert 404 == 200
 +  where 404 = <Response [404 Not Found]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 91 passed, 3 deselected in 0.82s
operator: core/RemoveDecorator, occurrence: 4
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -232,9 +232,6 @@
         Dictionary with health status.
     """
     return {"status": "healthy"}
-
-
-@app.get("/metrics")
 async def metrics() -> Response:
     """Prometheus metrics endpoint.
 
........................................................................ [ 34%]
.......................F
=================================== FAILURES ===================================
____________________________ test_metrics_endpoint _____________________________

test_client = <starlette.testclient.TestClient object at 0x7fca431b5810>

    def test_metrics_endpoint(test_client: TestClient):
        """Test Prometheus metrics endpoint."""
        response = test_client.get("/metrics")
>       assert response.status_code == 200
E       assert 404 == 200
E        +  where 404 = <Response [404 Not Found]>.status_code

tests/test_main.py:62: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:12:10.151468Z [info     ] HTTP Request: GET http://testserver/metrics "HTTP/1.1 404 Not Found"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: GET http://testserver/metrics "HTTP/1.1 404 Not Found"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_metrics_endpoint - assert 404 == 200
 +  where 404 = <Response [404 Not Found]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 95 passed, 3 deselected in 0.84s
operator: core/RemoveDecorator, occurrence: 5
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -245,9 +245,6 @@
         content=get_metrics().decode("utf-8"),
         media_type="text/plain",
     )
-
-
-@app.post("/webhook")
 async def webhook(
     request: Request,
     x_github_event: str = Header(None, alias="X-GitHub-Event"),
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7f7d08d9b770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
        response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )
        # Will fail signature validation, but Content-Length tracking happens before that
>       assert response.status_code == 401
E       assert 404 == 401
E        +  where 404 = <Response [404 Not Found]>.status_code

tests/test_main.py:79: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:33:05.022953Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 404 Not Found"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 404 Not Found"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - assert 404 == 401
 +  where 404 = <Response [404 Not Found]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 0.84s
operator: core/RemoveDecorator, occurrence: 6
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -325,14 +325,6 @@
         sanitized = _sanitize_error(e)
         logger.error("Error handling webhook event: %s", sanitized, extra={"error": sanitized})
         raise HTTPException(status_code=500, detail="Internal server error") from None
-
-
-# =============================================================================
-# Setup Endpoints - GitHub App Manifest Flow
-# =============================================================================
-
-
-@app.get("/setup")
 async def setup_page(request: Request) -> Response:
     """Setup page with manifest creation button.
 
........................................................................ [ 34%]
...........................................................F
=================================== FAILURES ===================================
_______ TestSetupEndpointsConfigured.test_setup_shows_already_configured _______

self = <tests.test_setup_endpoints.TestSetupEndpointsConfigured object at 0x7fe7d8aab9d0>
test_client = <starlette.testclient.TestClient object at 0x7fe7d421ead0>

    def test_setup_shows_already_configured(self, test_client):
        """Test /setup shows already configured message."""
        response = test_client.get("/setup")
    
>       assert response.status_code == 200
E       assert 404 == 200
E        +  where 404 = <Response [404 Not Found]>.status_code

tests/test_setup_endpoints.py:22: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:00:58.379818Z [info     ] HTTP Request: GET http://testserver/setup "HTTP/1.1 404 Not Found"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: GET http://testserver/setup "HTTP/1.1 404 Not Found"
=========================== short test summary info ============================
FAILED tests/test_setup_endpoints.py::TestSetupEndpointsConfigured::test_setup_shows_already_configured - assert 404 == 200
 +  where 404 = <Response [404 Not Found]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 131 passed, 3 deselected in 0.98s
operator: core/RemoveDecorator, occurrence: 7
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -448,9 +448,6 @@
     """
 
     return HTMLResponse(content=html_content)
-
-
-@app.get("/setup/callback")
 async def setup_callback(request: Request, code: str) -> Response:
     """Handle callback from GitHub after app creation.
 
........................................................................ [ 34%]
..................................................................F
=================================== FAILURES ===================================
________________ TestSetupCallback.test_callback_exchanges_code ________________

self = <tests.test_setup_endpoints.TestSetupCallback object at 0x7fcffd5fbd90>
test_client = <starlette.testclient.TestClient object at 0x7fcff8f23790>

    def test_callback_exchanges_code(self, test_client):
        """Test callback exchanges code for credentials."""
        mock_credentials = {
            "id": 12345,
            "pem": "-----BEGIN RSA PRIVATE KEY-----\ntest\n-----END RSA PRIVATE KEY-----",
            "webhook_secret": "test-secret",
            "slug": "test-stampbot",
            "name": "Test Stampbot",
        }
    
        with patch(
            "stampbot.main.exchange_code_for_credentials",
            new_callable=AsyncMock,
            return_value=mock_credentials,
        ):
            response = test_client.get("/setup/callback?code=test-code")
    
>           assert response.status_code == 200
E           assert 404 == 200
E            +  where 404 = <Response [404 Not Found]>.status_code

tests/test_setup_endpoints.py:179: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:42:10.564862Z [info     ] HTTP Request: GET http://testserver/setup/callback?code=test-code "HTTP/1.1 404 Not Found"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: GET http://testserver/setup/callback?code=test-code "HTTP/1.1 404 Not Found"
=========================== short test summary info ============================
FAILED tests/test_setup_endpoints.py::TestSetupCallback::test_callback_exchanges_code - assert 404 == 200
 +  where 404 = <Response [404 Not Found]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 138 passed, 3 deselected in 1.02s
operator: core/RemoveDecorator, occurrence: 8
--- mutation diff ---
--- astampbot/main.py
+++ bstampbot/main.py
@@ -561,9 +561,6 @@
     """
 
     return HTMLResponse(content=html_content)
-
-
-@app.get("/setup/status")
 async def setup_status() -> dict[str, Any]:
     """Check setup status.
 
........................................................................ [ 34%]
............................................................F
=================================== FAILURES ===================================
_______ TestSetupEndpointsConfigured.test_setup_status_shows_configured ________

self = <tests.test_setup_endpoints.TestSetupEndpointsConfigured object at 0x7f6e12d43360>
test_client = <starlette.testclient.TestClient object at 0x7f6e0db0e7a0>

    def test_setup_status_shows_configured(self, test_client):
        """Test /setup/status shows configured status."""
        response = test_client.get("/setup/status")
    
>       assert response.status_code == 200
E       assert 404 == 200
E        +  where 404 = <Response [404 Not Found]>.status_code

tests/test_setup_endpoints.py:29: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:45:26.081037Z [info     ] HTTP Request: GET http://testserver/setup/status "HTTP/1.1 404 Not Found"
------------------------------ Captured log call -------------------------------
INFO     httpx:_client.py:1025 HTTP Request: GET http://testserver/setup/status "HTTP/1.1 404 Not Found"
=========================== short test summary info ============================
FAILED tests/test_setup_endpoints.py::TestSetupEndpointsConfigured::test_setup_status_shows_configured - assert 404 == 200
 +  where 404 = <Response [404 Not Found]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 132 passed, 3 deselected in 0.99s
operator: core/ReplaceComparisonOperator_Eq_NotEq, occurrence: 0
--- mutation diff ---
--- astampbot/logger.py
+++ bstampbot/logger.py
@@ -33,7 +33,7 @@
     """
     log_format = settings.log_format.lower()
 
-    if log_format == "json":
+    if log_format != "json":
         return structlog.processors.JSONRenderer()
     elif log_format == "console":
         return structlog.dev.ConsoleRenderer(colors=True)
........................................................................ [ 34%]
...........F
=================================== FAILURES ===================================
________________________ test_get_log_renderer_console _________________________

    def test_get_log_renderer_console():
        """Test console renderer selection."""
        with patch("stampbot.logger.settings") as mock_settings:
            mock_settings.log_format = "console"
            from stampbot.logger import _get_log_renderer
    
            renderer = _get_log_renderer()
>           assert isinstance(renderer, structlog.dev.ConsoleRenderer)
E           AssertionError: assert False
E            +  where False = isinstance(<structlog.processors.JSONRenderer object at 0x7fde1e97c910>, <class 'structlog.dev.ConsoleRenderer'>)
E            +    where <class 'structlog.dev.ConsoleRenderer'> = <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'>.ConsoleRenderer
E            +      where <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'> = structlog.dev

tests/test_logger.py:46: AssertionError
=========================== short test summary info ============================
FAILED tests/test_logger.py::test_get_log_renderer_console - AssertionError: assert False
 +  where False = isinstance(<structlog.processors.JSONRenderer object at 0x7fde1e97c910>, <class 'structlog.dev.ConsoleRenderer'>)
 +    where <class 'structlog.dev.ConsoleRenderer'> = <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'>.ConsoleRenderer
 +      where <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'> = structlog.dev
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 83 passed, 3 deselected in 0.78s
operator: core/ReplaceComparisonOperator_Eq_NotEq, occurrence: 1
--- mutation diff ---
--- astampbot/logger.py
+++ bstampbot/logger.py
@@ -35,7 +35,7 @@
 
     if log_format == "json":
         return structlog.processors.JSONRenderer()
-    elif log_format == "console":
+    elif log_format != "console":
         return structlog.dev.ConsoleRenderer(colors=True)
     elif log_format == "auto":
         # Auto-detect: use JSON in Kubernetes, console otherwise
........................................................................ [ 34%]
...........F
=================================== FAILURES ===================================
________________________ test_get_log_renderer_console _________________________

    def test_get_log_renderer_console():
        """Test console renderer selection."""
        with patch("stampbot.logger.settings") as mock_settings:
            mock_settings.log_format = "console"
            from stampbot.logger import _get_log_renderer
    
            renderer = _get_log_renderer()
>           assert isinstance(renderer, structlog.dev.ConsoleRenderer)
E           AssertionError: assert False
E            +  where False = isinstance(<structlog.processors.JSONRenderer object at 0x7feb56e58910>, <class 'structlog.dev.ConsoleRenderer'>)
E            +    where <class 'structlog.dev.ConsoleRenderer'> = <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'>.ConsoleRenderer
E            +      where <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'> = structlog.dev

tests/test_logger.py:46: AssertionError
=========================== short test summary info ============================
FAILED tests/test_logger.py::test_get_log_renderer_console - AssertionError: assert False
 +  where False = isinstance(<structlog.processors.JSONRenderer object at 0x7feb56e58910>, <class 'structlog.dev.ConsoleRenderer'>)
 +    where <class 'structlog.dev.ConsoleRenderer'> = <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'>.ConsoleRenderer
 +      where <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'> = structlog.dev
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 83 passed, 3 deselected in 0.77s
operator: core/ReplaceComparisonOperator_Eq_NotEq, occurrence: 2
--- mutation diff ---
--- astampbot/logger.py
+++ bstampbot/logger.py
@@ -37,7 +37,7 @@
         return structlog.processors.JSONRenderer()
     elif log_format == "console":
         return structlog.dev.ConsoleRenderer(colors=True)
-    elif log_format == "auto":
+    elif log_format != "auto":
         # Auto-detect: use JSON in Kubernetes, console otherwise
         if is_running_in_kubernetes():
             return structlog.processors.JSONRenderer()
........................................................................ [ 34%]
.............F
=================================== FAILURES ===================================
_______________________ test_get_log_renderer_auto_local _______________________

    def test_get_log_renderer_auto_local():
        """Test auto renderer in local environment."""
        with patch("stampbot.logger.settings") as mock_settings:
            mock_settings.log_format = "auto"
            with patch("stampbot.logger.is_running_in_kubernetes", return_value=False):
                from stampbot.logger import _get_log_renderer
    
                renderer = _get_log_renderer()
>               assert isinstance(renderer, structlog.dev.ConsoleRenderer)
E               AssertionError: assert False
E                +  where False = isinstance(<structlog.processors.JSONRenderer object at 0x7f2575838b90>, <class 'structlog.dev.ConsoleRenderer'>)
E                +    where <class 'structlog.dev.ConsoleRenderer'> = <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'>.ConsoleRenderer
E                +      where <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'> = structlog.dev

tests/test_logger.py:68: AssertionError
=========================== short test summary info ============================
FAILED tests/test_logger.py::test_get_log_renderer_auto_local - AssertionError: assert False
 +  where False = isinstance(<structlog.processors.JSONRenderer object at 0x7f2575838b90>, <class 'structlog.dev.ConsoleRenderer'>)
 +    where <class 'structlog.dev.ConsoleRenderer'> = <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'>.ConsoleRenderer
 +      where <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'> = structlog.dev
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 85 passed, 3 deselected in 0.79s
operator: core/ReplaceComparisonOperator_Eq_Lt, occurrence: 0
--- mutation diff ---
--- astampbot/logger.py
+++ bstampbot/logger.py
@@ -33,7 +33,7 @@
     """
     log_format = settings.log_format.lower()
 
-    if log_format == "json":
+    if log_format < "json":
         return structlog.processors.JSONRenderer()
     elif log_format == "console":
         return structlog.dev.ConsoleRenderer(colors=True)
........................................................................ [ 34%]
...........F
=================================== FAILURES ===================================
________________________ test_get_log_renderer_console _________________________

    def test_get_log_renderer_console():
        """Test console renderer selection."""
        with patch("stampbot.logger.settings") as mock_settings:
            mock_settings.log_format = "console"
            from stampbot.logger import _get_log_renderer
    
            renderer = _get_log_renderer()
>           assert isinstance(renderer, structlog.dev.ConsoleRenderer)
E           AssertionError: assert False
E            +  where False = isinstance(<structlog.processors.JSONRenderer object at 0x7fb21345c910>, <class 'structlog.dev.ConsoleRenderer'>)
E            +    where <class 'structlog.dev.ConsoleRenderer'> = <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'>.ConsoleRenderer
E            +      where <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'> = structlog.dev

tests/test_logger.py:46: AssertionError
=========================== short test summary info ============================
FAILED tests/test_logger.py::test_get_log_renderer_console - AssertionError: assert False
 +  where False = isinstance(<structlog.processors.JSONRenderer object at 0x7fb21345c910>, <class 'structlog.dev.ConsoleRenderer'>)
 +    where <class 'structlog.dev.ConsoleRenderer'> = <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'>.ConsoleRenderer
 +      where <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'> = structlog.dev
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 83 passed, 3 deselected in 0.77s
operator: core/ReplaceComparisonOperator_Eq_Lt, occurrence: 1
--- mutation diff ---
--- astampbot/logger.py
+++ bstampbot/logger.py
@@ -35,7 +35,7 @@
 
     if log_format == "json":
         return structlog.processors.JSONRenderer()
-    elif log_format == "console":
+    elif log_format < "console":
         return structlog.dev.ConsoleRenderer(colors=True)
     elif log_format == "auto":
         # Auto-detect: use JSON in Kubernetes, console otherwise
........................................................................ [ 34%]
...........F
=================================== FAILURES ===================================
________________________ test_get_log_renderer_console _________________________

    def test_get_log_renderer_console():
        """Test console renderer selection."""
        with patch("stampbot.logger.settings") as mock_settings:
            mock_settings.log_format = "console"
            from stampbot.logger import _get_log_renderer
    
            renderer = _get_log_renderer()
>           assert isinstance(renderer, structlog.dev.ConsoleRenderer)
E           AssertionError: assert False
E            +  where False = isinstance(<structlog.processors.JSONRenderer object at 0x7faee8730910>, <class 'structlog.dev.ConsoleRenderer'>)
E            +    where <class 'structlog.dev.ConsoleRenderer'> = <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'>.ConsoleRenderer
E            +      where <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'> = structlog.dev

tests/test_logger.py:46: AssertionError
=========================== short test summary info ============================
FAILED tests/test_logger.py::test_get_log_renderer_console - AssertionError: assert False
 +  where False = isinstance(<structlog.processors.JSONRenderer object at 0x7faee8730910>, <class 'structlog.dev.ConsoleRenderer'>)
 +    where <class 'structlog.dev.ConsoleRenderer'> = <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'>.ConsoleRenderer
 +      where <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'> = structlog.dev
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 83 passed, 3 deselected in 0.77s
operator: core/ReplaceComparisonOperator_Eq_Lt, occurrence: 2
--- mutation diff ---
--- astampbot/logger.py
+++ bstampbot/logger.py
@@ -37,7 +37,7 @@
         return structlog.processors.JSONRenderer()
     elif log_format == "console":
         return structlog.dev.ConsoleRenderer(colors=True)
-    elif log_format == "auto":
+    elif log_format < "auto":
         # Auto-detect: use JSON in Kubernetes, console otherwise
         if is_running_in_kubernetes():
             return structlog.processors.JSONRenderer()
........................................................................ [ 34%]
.............F
=================================== FAILURES ===================================
_______________________ test_get_log_renderer_auto_local _______________________

    def test_get_log_renderer_auto_local():
        """Test auto renderer in local environment."""
        with patch("stampbot.logger.settings") as mock_settings:
            mock_settings.log_format = "auto"
            with patch("stampbot.logger.is_running_in_kubernetes", return_value=False):
                from stampbot.logger import _get_log_renderer
    
                renderer = _get_log_renderer()
>               assert isinstance(renderer, structlog.dev.ConsoleRenderer)
E               AssertionError: assert False
E                +  where False = isinstance(<structlog.processors.JSONRenderer object at 0x7fa4ccb68b90>, <class 'structlog.dev.ConsoleRenderer'>)
E                +    where <class 'structlog.dev.ConsoleRenderer'> = <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'>.ConsoleRenderer
E                +      where <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'> = structlog.dev

tests/test_logger.py:68: AssertionError
=========================== short test summary info ============================
FAILED tests/test_logger.py::test_get_log_renderer_auto_local - AssertionError: assert False
 +  where False = isinstance(<structlog.processors.JSONRenderer object at 0x7fa4ccb68b90>, <class 'structlog.dev.ConsoleRenderer'>)
 +    where <class 'structlog.dev.ConsoleRenderer'> = <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'>.ConsoleRenderer
 +      where <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'> = structlog.dev
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 85 passed, 3 deselected in 0.78s
operator: core/ReplaceComparisonOperator_Eq_LtE, occurrence: 0
--- mutation diff ---
--- astampbot/logger.py
+++ bstampbot/logger.py
@@ -33,7 +33,7 @@
     """
     log_format = settings.log_format.lower()
 
-    if log_format == "json":
+    if log_format <= "json":
         return structlog.processors.JSONRenderer()
     elif log_format == "console":
         return structlog.dev.ConsoleRenderer(colors=True)
........................................................................ [ 34%]
...........F
=================================== FAILURES ===================================
________________________ test_get_log_renderer_console _________________________

    def test_get_log_renderer_console():
        """Test console renderer selection."""
        with patch("stampbot.logger.settings") as mock_settings:
            mock_settings.log_format = "console"
            from stampbot.logger import _get_log_renderer
    
            renderer = _get_log_renderer()
>           assert isinstance(renderer, structlog.dev.ConsoleRenderer)
E           AssertionError: assert False
E            +  where False = isinstance(<structlog.processors.JSONRenderer object at 0x7f7032550910>, <class 'structlog.dev.ConsoleRenderer'>)
E            +    where <class 'structlog.dev.ConsoleRenderer'> = <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'>.ConsoleRenderer
E            +      where <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'> = structlog.dev

tests/test_logger.py:46: AssertionError
=========================== short test summary info ============================
FAILED tests/test_logger.py::test_get_log_renderer_console - AssertionError: assert False
 +  where False = isinstance(<structlog.processors.JSONRenderer object at 0x7f7032550910>, <class 'structlog.dev.ConsoleRenderer'>)
 +    where <class 'structlog.dev.ConsoleRenderer'> = <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'>.ConsoleRenderer
 +      where <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'> = structlog.dev
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 83 passed, 3 deselected in 0.77s
operator: core/ReplaceComparisonOperator_Eq_LtE, occurrence: 1
--- mutation diff ---
--- astampbot/logger.py
+++ bstampbot/logger.py
@@ -35,7 +35,7 @@
 
     if log_format == "json":
         return structlog.processors.JSONRenderer()
-    elif log_format == "console":
+    elif log_format <= "console":
         return structlog.dev.ConsoleRenderer(colors=True)
     elif log_format == "auto":
         # Auto-detect: use JSON in Kubernetes, console otherwise
........................................................................ [ 34%]
............F
=================================== FAILURES ===================================
____________________ test_get_log_renderer_auto_kubernetes _____________________

    def test_get_log_renderer_auto_kubernetes():
        """Test auto renderer in Kubernetes environment."""
        with patch("stampbot.logger.settings") as mock_settings:
            mock_settings.log_format = "auto"
            with patch("stampbot.logger.is_running_in_kubernetes", return_value=True):
                from stampbot.logger import _get_log_renderer
    
                renderer = _get_log_renderer()
>               assert isinstance(renderer, structlog.processors.JSONRenderer)
E               AssertionError: assert False
E                +  where False = isinstance(<structlog.dev.ConsoleRenderer object at 0x7fa887f70b90>, <class 'structlog.processors.JSONRenderer'>)
E                +    where <class 'structlog.processors.JSONRenderer'> = <module 'structlog.processors' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/processors.py'>.JSONRenderer
E                +      where <module 'structlog.processors' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/processors.py'> = structlog.processors

tests/test_logger.py:57: AssertionError
=========================== short test summary info ============================
FAILED tests/test_logger.py::test_get_log_renderer_auto_kubernetes - AssertionError: assert False
 +  where False = isinstance(<structlog.dev.ConsoleRenderer object at 0x7fa887f70b90>, <class 'structlog.processors.JSONRenderer'>)
 +    where <class 'structlog.processors.JSONRenderer'> = <module 'structlog.processors' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/processors.py'>.JSONRenderer
 +      where <module 'structlog.processors' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/processors.py'> = structlog.processors
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 84 passed, 3 deselected in 0.78s
operator: core/ReplaceComparisonOperator_Eq_LtE, occurrence: 2
--- mutation diff ---
--- astampbot/logger.py
+++ bstampbot/logger.py
@@ -37,7 +37,7 @@
         return structlog.processors.JSONRenderer()
     elif log_format == "console":
         return structlog.dev.ConsoleRenderer(colors=True)
-    elif log_format == "auto":
+    elif log_format <= "auto":
         # Auto-detect: use JSON in Kubernetes, console otherwise
         if is_running_in_kubernetes():
             return structlog.processors.JSONRenderer()
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceComparisonOperator_Eq_Gt, occurrence: 0
--- mutation diff ---
--- astampbot/logger.py
+++ bstampbot/logger.py
@@ -33,7 +33,7 @@
     """
     log_format = settings.log_format.lower()
 
-    if log_format == "json":
+    if log_format > "json":
         return structlog.processors.JSONRenderer()
     elif log_format == "console":
         return structlog.dev.ConsoleRenderer(colors=True)
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceComparisonOperator_Eq_Gt, occurrence: 1
--- mutation diff ---
--- astampbot/logger.py
+++ bstampbot/logger.py
@@ -35,7 +35,7 @@
 
     if log_format == "json":
         return structlog.processors.JSONRenderer()
-    elif log_format == "console":
+    elif log_format > "console":
         return structlog.dev.ConsoleRenderer(colors=True)
     elif log_format == "auto":
         # Auto-detect: use JSON in Kubernetes, console otherwise
........................................................................ [ 34%]
...........F
=================================== FAILURES ===================================
________________________ test_get_log_renderer_console _________________________

    def test_get_log_renderer_console():
        """Test console renderer selection."""
        with patch("stampbot.logger.settings") as mock_settings:
            mock_settings.log_format = "console"
            from stampbot.logger import _get_log_renderer
    
            renderer = _get_log_renderer()
>           assert isinstance(renderer, structlog.dev.ConsoleRenderer)
E           AssertionError: assert False
E            +  where False = isinstance(<structlog.processors.JSONRenderer object at 0x7fc14975c910>, <class 'structlog.dev.ConsoleRenderer'>)
E            +    where <class 'structlog.dev.ConsoleRenderer'> = <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'>.ConsoleRenderer
E            +      where <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'> = structlog.dev

tests/test_logger.py:46: AssertionError
=========================== short test summary info ============================
FAILED tests/test_logger.py::test_get_log_renderer_console - AssertionError: assert False
 +  where False = isinstance(<structlog.processors.JSONRenderer object at 0x7fc14975c910>, <class 'structlog.dev.ConsoleRenderer'>)
 +    where <class 'structlog.dev.ConsoleRenderer'> = <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'>.ConsoleRenderer
 +      where <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'> = structlog.dev
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 83 passed, 3 deselected in 0.78s
operator: core/ReplaceComparisonOperator_Eq_Gt, occurrence: 2
--- mutation diff ---
--- astampbot/logger.py
+++ bstampbot/logger.py
@@ -37,7 +37,7 @@
         return structlog.processors.JSONRenderer()
     elif log_format == "console":
         return structlog.dev.ConsoleRenderer(colors=True)
-    elif log_format == "auto":
+    elif log_format > "auto":
         # Auto-detect: use JSON in Kubernetes, console otherwise
         if is_running_in_kubernetes():
             return structlog.processors.JSONRenderer()
........................................................................ [ 34%]
.............F
=================================== FAILURES ===================================
_______________________ test_get_log_renderer_auto_local _______________________

    def test_get_log_renderer_auto_local():
        """Test auto renderer in local environment."""
        with patch("stampbot.logger.settings") as mock_settings:
            mock_settings.log_format = "auto"
            with patch("stampbot.logger.is_running_in_kubernetes", return_value=False):
                from stampbot.logger import _get_log_renderer
    
                renderer = _get_log_renderer()
>               assert isinstance(renderer, structlog.dev.ConsoleRenderer)
E               AssertionError: assert False
E                +  where False = isinstance(<structlog.processors.JSONRenderer object at 0x7fdd64258b90>, <class 'structlog.dev.ConsoleRenderer'>)
E                +    where <class 'structlog.dev.ConsoleRenderer'> = <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'>.ConsoleRenderer
E                +      where <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'> = structlog.dev

tests/test_logger.py:68: AssertionError
=========================== short test summary info ============================
FAILED tests/test_logger.py::test_get_log_renderer_auto_local - AssertionError: assert False
 +  where False = isinstance(<structlog.processors.JSONRenderer object at 0x7fdd64258b90>, <class 'structlog.dev.ConsoleRenderer'>)
 +    where <class 'structlog.dev.ConsoleRenderer'> = <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'>.ConsoleRenderer
 +      where <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'> = structlog.dev
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 85 passed, 3 deselected in 0.78s
operator: core/ReplaceComparisonOperator_Eq_GtE, occurrence: 0
--- mutation diff ---
--- astampbot/logger.py
+++ bstampbot/logger.py
@@ -33,7 +33,7 @@
     """
     log_format = settings.log_format.lower()
 
-    if log_format == "json":
+    if log_format >= "json":
         return structlog.processors.JSONRenderer()
     elif log_format == "console":
         return structlog.dev.ConsoleRenderer(colors=True)
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceComparisonOperator_Eq_GtE, occurrence: 1
--- mutation diff ---
--- astampbot/logger.py
+++ bstampbot/logger.py
@@ -35,7 +35,7 @@
 
     if log_format == "json":
         return structlog.processors.JSONRenderer()
-    elif log_format == "console":
+    elif log_format >= "console":
         return structlog.dev.ConsoleRenderer(colors=True)
     elif log_format == "auto":
         # Auto-detect: use JSON in Kubernetes, console otherwise
........................................................................ [ 34%]
..............F
=================================== FAILURES ===================================
_____________________ test_get_log_renderer_unknown_format _____________________

    def test_get_log_renderer_unknown_format():
        """Test fallback to JSON for unknown format."""
        with patch("stampbot.logger.settings") as mock_settings:
            mock_settings.log_format = "unknown_format"
            from stampbot.logger import _get_log_renderer
    
            renderer = _get_log_renderer()
>           assert isinstance(renderer, structlog.processors.JSONRenderer)
E           AssertionError: assert False
E            +  where False = isinstance(<structlog.dev.ConsoleRenderer object at 0x7f3f6dc04b00>, <class 'structlog.processors.JSONRenderer'>)
E            +    where <class 'structlog.processors.JSONRenderer'> = <module 'structlog.processors' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/processors.py'>.JSONRenderer
E            +      where <module 'structlog.processors' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/processors.py'> = structlog.processors

tests/test_logger.py:78: AssertionError
=========================== short test summary info ============================
FAILED tests/test_logger.py::test_get_log_renderer_unknown_format - AssertionError: assert False
 +  where False = isinstance(<structlog.dev.ConsoleRenderer object at 0x7f3f6dc04b00>, <class 'structlog.processors.JSONRenderer'>)
 +    where <class 'structlog.processors.JSONRenderer'> = <module 'structlog.processors' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/processors.py'>.JSONRenderer
 +      where <module 'structlog.processors' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/processors.py'> = structlog.processors
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 86 passed, 3 deselected in 0.78s
operator: core/ReplaceComparisonOperator_Eq_GtE, occurrence: 2
--- mutation diff ---
--- astampbot/logger.py
+++ bstampbot/logger.py
@@ -37,7 +37,7 @@
         return structlog.processors.JSONRenderer()
     elif log_format == "console":
         return structlog.dev.ConsoleRenderer(colors=True)
-    elif log_format == "auto":
+    elif log_format >= "auto":
         # Auto-detect: use JSON in Kubernetes, console otherwise
         if is_running_in_kubernetes():
             return structlog.processors.JSONRenderer()
........................................................................ [ 34%]
..............F
=================================== FAILURES ===================================
_____________________ test_get_log_renderer_unknown_format _____________________

    def test_get_log_renderer_unknown_format():
        """Test fallback to JSON for unknown format."""
        with patch("stampbot.logger.settings") as mock_settings:
            mock_settings.log_format = "unknown_format"
            from stampbot.logger import _get_log_renderer
    
            renderer = _get_log_renderer()
>           assert isinstance(renderer, structlog.processors.JSONRenderer)
E           AssertionError: assert False
E            +  where False = isinstance(<structlog.dev.ConsoleRenderer object at 0x7f0433a00b00>, <class 'structlog.processors.JSONRenderer'>)
E            +    where <class 'structlog.processors.JSONRenderer'> = <module 'structlog.processors' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/processors.py'>.JSONRenderer
E            +      where <module 'structlog.processors' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/processors.py'> = structlog.processors

tests/test_logger.py:78: AssertionError
=========================== short test summary info ============================
FAILED tests/test_logger.py::test_get_log_renderer_unknown_format - AssertionError: assert False
 +  where False = isinstance(<structlog.dev.ConsoleRenderer object at 0x7f0433a00b00>, <class 'structlog.processors.JSONRenderer'>)
 +    where <class 'structlog.processors.JSONRenderer'> = <module 'structlog.processors' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/processors.py'>.JSONRenderer
 +      where <module 'structlog.processors' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/processors.py'> = structlog.processors
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 86 passed, 3 deselected in 0.78s
operator: core/ReplaceComparisonOperator_Eq_Is, occurrence: 0
--- mutation diff ---
--- astampbot/logger.py
+++ bstampbot/logger.py
@@ -33,7 +33,7 @@
     """
     log_format = settings.log_format.lower()
 
-    if log_format == "json":
+    if log_format is "json":
         return structlog.processors.JSONRenderer()
     elif log_format == "console":
         return structlog.dev.ConsoleRenderer(colors=True)
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
=============================== warnings summary ===============================
stampbot/logger.py:36
  /home/runner/work/stampbot/stampbot/stampbot/logger.py:36: SyntaxWarning: "is" with 'str' literal. Did you mean "=="?
    if log_format is "json":

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
211 passed, 3 deselected, 1 warning in 1.62s
operator: core/ReplaceComparisonOperator_Eq_Is, occurrence: 1
--- mutation diff ---
--- astampbot/logger.py
+++ bstampbot/logger.py
@@ -35,7 +35,7 @@
 
     if log_format == "json":
         return structlog.processors.JSONRenderer()
-    elif log_format == "console":
+    elif log_format is "console":
         return structlog.dev.ConsoleRenderer(colors=True)
     elif log_format == "auto":
         # Auto-detect: use JSON in Kubernetes, console otherwise
........................................................................ [ 34%]
...........F
=================================== FAILURES ===================================
________________________ test_get_log_renderer_console _________________________

    def test_get_log_renderer_console():
        """Test console renderer selection."""
        with patch("stampbot.logger.settings") as mock_settings:
            mock_settings.log_format = "console"
            from stampbot.logger import _get_log_renderer
    
            renderer = _get_log_renderer()
>           assert isinstance(renderer, structlog.dev.ConsoleRenderer)
E           AssertionError: assert False
E            +  where False = isinstance(<structlog.processors.JSONRenderer object at 0x7fb42ed60910>, <class 'structlog.dev.ConsoleRenderer'>)
E            +    where <class 'structlog.dev.ConsoleRenderer'> = <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'>.ConsoleRenderer
E            +      where <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'> = structlog.dev

tests/test_logger.py:46: AssertionError
=============================== warnings summary ===============================
stampbot/logger.py:38
  /home/runner/work/stampbot/stampbot/stampbot/logger.py:38: SyntaxWarning: "is" with 'str' literal. Did you mean "=="?
    elif log_format is "console":

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=========================== short test summary info ============================
FAILED tests/test_logger.py::test_get_log_renderer_console - AssertionError: assert False
 +  where False = isinstance(<structlog.processors.JSONRenderer object at 0x7fb42ed60910>, <class 'structlog.dev.ConsoleRenderer'>)
 +    where <class 'structlog.dev.ConsoleRenderer'> = <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'>.ConsoleRenderer
 +      where <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'> = structlog.dev
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 83 passed, 3 deselected, 1 warning in 0.78s
operator: core/ReplaceComparisonOperator_Eq_Is, occurrence: 2
--- mutation diff ---
--- astampbot/logger.py
+++ bstampbot/logger.py
@@ -37,7 +37,7 @@
         return structlog.processors.JSONRenderer()
     elif log_format == "console":
         return structlog.dev.ConsoleRenderer(colors=True)
-    elif log_format == "auto":
+    elif log_format is "auto":
         # Auto-detect: use JSON in Kubernetes, console otherwise
         if is_running_in_kubernetes():
             return structlog.processors.JSONRenderer()
........................................................................ [ 34%]
.............F
=================================== FAILURES ===================================
_______________________ test_get_log_renderer_auto_local _______________________

    def test_get_log_renderer_auto_local():
        """Test auto renderer in local environment."""
        with patch("stampbot.logger.settings") as mock_settings:
            mock_settings.log_format = "auto"
            with patch("stampbot.logger.is_running_in_kubernetes", return_value=False):
                from stampbot.logger import _get_log_renderer
    
                renderer = _get_log_renderer()
>               assert isinstance(renderer, structlog.dev.ConsoleRenderer)
E               AssertionError: assert False
E                +  where False = isinstance(<structlog.processors.JSONRenderer object at 0x7f695c45cb90>, <class 'structlog.dev.ConsoleRenderer'>)
E                +    where <class 'structlog.dev.ConsoleRenderer'> = <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'>.ConsoleRenderer
E                +      where <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'> = structlog.dev

tests/test_logger.py:68: AssertionError
=============================== warnings summary ===============================
stampbot/logger.py:40
  /home/runner/work/stampbot/stampbot/stampbot/logger.py:40: SyntaxWarning: "is" with 'str' literal. Did you mean "=="?
    elif log_format is "auto":

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=========================== short test summary info ============================
FAILED tests/test_logger.py::test_get_log_renderer_auto_local - AssertionError: assert False
 +  where False = isinstance(<structlog.processors.JSONRenderer object at 0x7f695c45cb90>, <class 'structlog.dev.ConsoleRenderer'>)
 +    where <class 'structlog.dev.ConsoleRenderer'> = <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'>.ConsoleRenderer
 +      where <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'> = structlog.dev
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 85 passed, 3 deselected, 1 warning in 0.77s
operator: core/ReplaceComparisonOperator_Eq_IsNot, occurrence: 0
--- mutation diff ---
--- astampbot/logger.py
+++ bstampbot/logger.py
@@ -33,7 +33,7 @@
     """
     log_format = settings.log_format.lower()
 
-    if log_format == "json":
+    if log_format is not "json":
         return structlog.processors.JSONRenderer()
     elif log_format == "console":
         return structlog.dev.ConsoleRenderer(colors=True)
........................................................................ [ 34%]
...........F
=================================== FAILURES ===================================
________________________ test_get_log_renderer_console _________________________

    def test_get_log_renderer_console():
        """Test console renderer selection."""
        with patch("stampbot.logger.settings") as mock_settings:
            mock_settings.log_format = "console"
            from stampbot.logger import _get_log_renderer
    
            renderer = _get_log_renderer()
>           assert isinstance(renderer, structlog.dev.ConsoleRenderer)
E           AssertionError: assert False
E            +  where False = isinstance(<structlog.processors.JSONRenderer object at 0x7f160c84c910>, <class 'structlog.dev.ConsoleRenderer'>)
E            +    where <class 'structlog.dev.ConsoleRenderer'> = <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'>.ConsoleRenderer
E            +      where <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'> = structlog.dev

tests/test_logger.py:46: AssertionError
=============================== warnings summary ===============================
stampbot/logger.py:36
  /home/runner/work/stampbot/stampbot/stampbot/logger.py:36: SyntaxWarning: "is not" with 'str' literal. Did you mean "!="?
    if log_format is not "json":

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=========================== short test summary info ============================
FAILED tests/test_logger.py::test_get_log_renderer_console - AssertionError: assert False
 +  where False = isinstance(<structlog.processors.JSONRenderer object at 0x7f160c84c910>, <class 'structlog.dev.ConsoleRenderer'>)
 +    where <class 'structlog.dev.ConsoleRenderer'> = <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'>.ConsoleRenderer
 +      where <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'> = structlog.dev
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 83 passed, 3 deselected, 1 warning in 0.78s
operator: core/ReplaceComparisonOperator_Eq_IsNot, occurrence: 1
--- mutation diff ---
--- astampbot/logger.py
+++ bstampbot/logger.py
@@ -35,7 +35,7 @@
 
     if log_format == "json":
         return structlog.processors.JSONRenderer()
-    elif log_format == "console":
+    elif log_format is not "console":
         return structlog.dev.ConsoleRenderer(colors=True)
     elif log_format == "auto":
         # Auto-detect: use JSON in Kubernetes, console otherwise
........................................................................ [ 34%]
............F
=================================== FAILURES ===================================
____________________ test_get_log_renderer_auto_kubernetes _____________________

    def test_get_log_renderer_auto_kubernetes():
        """Test auto renderer in Kubernetes environment."""
        with patch("stampbot.logger.settings") as mock_settings:
            mock_settings.log_format = "auto"
            with patch("stampbot.logger.is_running_in_kubernetes", return_value=True):
                from stampbot.logger import _get_log_renderer
    
                renderer = _get_log_renderer()
>               assert isinstance(renderer, structlog.processors.JSONRenderer)
E               AssertionError: assert False
E                +  where False = isinstance(<structlog.dev.ConsoleRenderer object at 0x7f9ae6454b90>, <class 'structlog.processors.JSONRenderer'>)
E                +    where <class 'structlog.processors.JSONRenderer'> = <module 'structlog.processors' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/processors.py'>.JSONRenderer
E                +      where <module 'structlog.processors' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/processors.py'> = structlog.processors

tests/test_logger.py:57: AssertionError
=============================== warnings summary ===============================
stampbot/logger.py:38
  /home/runner/work/stampbot/stampbot/stampbot/logger.py:38: SyntaxWarning: "is not" with 'str' literal. Did you mean "!="?
    elif log_format is not "console":

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=========================== short test summary info ============================
FAILED tests/test_logger.py::test_get_log_renderer_auto_kubernetes - AssertionError: assert False
 +  where False = isinstance(<structlog.dev.ConsoleRenderer object at 0x7f9ae6454b90>, <class 'structlog.processors.JSONRenderer'>)
 +    where <class 'structlog.processors.JSONRenderer'> = <module 'structlog.processors' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/processors.py'>.JSONRenderer
 +      where <module 'structlog.processors' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/processors.py'> = structlog.processors
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 84 passed, 3 deselected, 1 warning in 0.77s
operator: core/ReplaceComparisonOperator_Eq_IsNot, occurrence: 2
--- mutation diff ---
--- astampbot/logger.py
+++ bstampbot/logger.py
@@ -37,7 +37,7 @@
         return structlog.processors.JSONRenderer()
     elif log_format == "console":
         return structlog.dev.ConsoleRenderer(colors=True)
-    elif log_format == "auto":
+    elif log_format is not "auto":
         # Auto-detect: use JSON in Kubernetes, console otherwise
         if is_running_in_kubernetes():
             return structlog.processors.JSONRenderer()
........................................................................ [ 34%]
..............F
=================================== FAILURES ===================================
_____________________ test_get_log_renderer_unknown_format _____________________

    def test_get_log_renderer_unknown_format():
        """Test fallback to JSON for unknown format."""
        with patch("stampbot.logger.settings") as mock_settings:
            mock_settings.log_format = "unknown_format"
            from stampbot.logger import _get_log_renderer
    
            renderer = _get_log_renderer()
>           assert isinstance(renderer, structlog.processors.JSONRenderer)
E           AssertionError: assert False
E            +  where False = isinstance(<structlog.dev.ConsoleRenderer object at 0x7f8cefa04b00>, <class 'structlog.processors.JSONRenderer'>)
E            +    where <class 'structlog.processors.JSONRenderer'> = <module 'structlog.processors' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/processors.py'>.JSONRenderer
E            +      where <module 'structlog.processors' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/processors.py'> = structlog.processors

tests/test_logger.py:78: AssertionError
=============================== warnings summary ===============================
stampbot/logger.py:40
  /home/runner/work/stampbot/stampbot/stampbot/logger.py:40: SyntaxWarning: "is not" with 'str' literal. Did you mean "!="?
    elif log_format is not "auto":

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=========================== short test summary info ============================
FAILED tests/test_logger.py::test_get_log_renderer_unknown_format - AssertionError: assert False
 +  where False = isinstance(<structlog.dev.ConsoleRenderer object at 0x7f8cefa04b00>, <class 'structlog.processors.JSONRenderer'>)
 +    where <class 'structlog.processors.JSONRenderer'> = <module 'structlog.processors' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/processors.py'>.JSONRenderer
 +      where <module 'structlog.processors' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/processors.py'> = structlog.processors
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 86 passed, 3 deselected, 1 warning in 0.77s
operator: core/AddNot, occurrence: 0
--- mutation diff ---
--- astampbot/logger.py
+++ bstampbot/logger.py
@@ -33,7 +33,7 @@
     """
     log_format = settings.log_format.lower()
 
-    if log_format == "json":
+    if not log_format == "json":
         return structlog.processors.JSONRenderer()
     elif log_format == "console":
         return structlog.dev.ConsoleRenderer(colors=True)
........................................................................ [ 34%]
...........F
=================================== FAILURES ===================================
________________________ test_get_log_renderer_console _________________________

    def test_get_log_renderer_console():
        """Test console renderer selection."""
        with patch("stampbot.logger.settings") as mock_settings:
            mock_settings.log_format = "console"
            from stampbot.logger import _get_log_renderer
    
            renderer = _get_log_renderer()
>           assert isinstance(renderer, structlog.dev.ConsoleRenderer)
E           AssertionError: assert False
E            +  where False = isinstance(<structlog.processors.JSONRenderer object at 0x7fa873258910>, <class 'structlog.dev.ConsoleRenderer'>)
E            +    where <class 'structlog.dev.ConsoleRenderer'> = <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'>.ConsoleRenderer
E            +      where <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'> = structlog.dev

tests/test_logger.py:46: AssertionError
=========================== short test summary info ============================
FAILED tests/test_logger.py::test_get_log_renderer_console - AssertionError: assert False
 +  where False = isinstance(<structlog.processors.JSONRenderer object at 0x7fa873258910>, <class 'structlog.dev.ConsoleRenderer'>)
 +    where <class 'structlog.dev.ConsoleRenderer'> = <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'>.ConsoleRenderer
 +      where <module 'structlog.dev' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/dev.py'> = structlog.dev
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 83 passed, 3 deselected in 0.77s
operator: core/AddNot, occurrence: 1
--- mutation diff ---
--- astampbot/logger.py
+++ bstampbot/logger.py
@@ -39,7 +39,7 @@
         return structlog.dev.ConsoleRenderer(colors=True)
     elif log_format == "auto":
         # Auto-detect: use JSON in Kubernetes, console otherwise
-        if is_running_in_kubernetes():
+        if not is_running_in_kubernetes():
             return structlog.processors.JSONRenderer()
         else:
             return structlog.dev.ConsoleRenderer(colors=True)
........................................................................ [ 34%]
............F
=================================== FAILURES ===================================
____________________ test_get_log_renderer_auto_kubernetes _____________________

    def test_get_log_renderer_auto_kubernetes():
        """Test auto renderer in Kubernetes environment."""
        with patch("stampbot.logger.settings") as mock_settings:
            mock_settings.log_format = "auto"
            with patch("stampbot.logger.is_running_in_kubernetes", return_value=True):
                from stampbot.logger import _get_log_renderer
    
                renderer = _get_log_renderer()
>               assert isinstance(renderer, structlog.processors.JSONRenderer)
E               AssertionError: assert False
E                +  where False = isinstance(<structlog.dev.ConsoleRenderer object at 0x7f4096454b90>, <class 'structlog.processors.JSONRenderer'>)
E                +    where <class 'structlog.processors.JSONRenderer'> = <module 'structlog.processors' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/processors.py'>.JSONRenderer
E                +      where <module 'structlog.processors' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/processors.py'> = structlog.processors

tests/test_logger.py:57: AssertionError
=========================== short test summary info ============================
FAILED tests/test_logger.py::test_get_log_renderer_auto_kubernetes - AssertionError: assert False
 +  where False = isinstance(<structlog.dev.ConsoleRenderer object at 0x7f4096454b90>, <class 'structlog.processors.JSONRenderer'>)
 +    where <class 'structlog.processors.JSONRenderer'> = <module 'structlog.processors' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/processors.py'>.JSONRenderer
 +      where <module 'structlog.processors' from '/home/runner/.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/structlog/processors.py'> = structlog.processors
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 84 passed, 3 deselected in 0.79s
operator: core/AddNot, occurrence: 2
--- mutation diff ---
--- astampbot/logger.py
+++ bstampbot/logger.py
@@ -100,7 +100,7 @@
     root_logger.setLevel(log_level_int)
 
     # Instrument logging with OpenTelemetry if enabled
-    if settings.otel_enabled:
+    if not settings.otel_enabled:
         LoggingInstrumentor().instrument(set_logging_format=True)
 
 
........................................................................ [ 34%]
................F
=================================== FAILURES ===================================
___________________ test_configure_logging_with_otel_enabled ___________________

    def test_configure_logging_with_otel_enabled():
        """Test configure_logging instruments logging when OTEL is enabled."""
        with (
            patch("stampbot.logger.settings") as mock_settings,
            patch("stampbot.logger.LoggingInstrumentor") as mock_instrumentor,
        ):
            mock_settings.log_format = "json"
            mock_settings.log_level = "INFO"
            mock_settings.otel_enabled = True
    
            from stampbot.logger import configure_logging
    
            configure_logging()
    
            # Verify LoggingInstrumentor was called
>           mock_instrumentor.return_value.instrument.assert_called_once_with(set_logging_format=True)

tests/test_logger.py:104: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <MagicMock name='LoggingInstrumentor().instrument' id='139914293011392'>
args = (), kwargs = {'set_logging_format': True}
msg = "Expected 'instrument' to be called once. Called 0 times."

    def assert_called_once_with(self, /, *args, **kwargs):
        """assert that the mock was called exactly once and that that call was
        with the specified arguments."""
        if not self.call_count == 1:
            msg = ("Expected '%s' to be called once. Called %s times.%s"
                   % (self._mock_name or 'mock',
                      self.call_count,
                      self._calls_repr()))
>           raise AssertionError(msg)
E           AssertionError: Expected 'instrument' to be called once. Called 0 times.

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:996: AssertionError
=========================== short test summary info ============================
FAILED tests/test_logger.py::test_configure_logging_with_otel_enabled - AssertionError: Expected 'instrument' to be called once. Called 0 times.
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 88 passed, 3 deselected in 0.82s
operator: core/ReplaceTrueWithFalse, occurrence: 0
--- mutation diff ---
--- astampbot/logger.py
+++ bstampbot/logger.py
@@ -36,7 +36,7 @@
     if log_format == "json":
         return structlog.processors.JSONRenderer()
     elif log_format == "console":
-        return structlog.dev.ConsoleRenderer(colors=True)
+        return structlog.dev.ConsoleRenderer(colors=False)
     elif log_format == "auto":
         # Auto-detect: use JSON in Kubernetes, console otherwise
         if is_running_in_kubernetes():
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceTrueWithFalse, occurrence: 1
--- mutation diff ---
--- astampbot/logger.py
+++ bstampbot/logger.py
@@ -42,7 +42,7 @@
         if is_running_in_kubernetes():
             return structlog.processors.JSONRenderer()
         else:
-            return structlog.dev.ConsoleRenderer(colors=True)
+            return structlog.dev.ConsoleRenderer(colors=False)
     else:
         # Unknown format, default to JSON for safety
         return structlog.processors.JSONRenderer()
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceTrueWithFalse, occurrence: 2
--- mutation diff ---
--- astampbot/logger.py
+++ bstampbot/logger.py
@@ -78,7 +78,7 @@
         context_class=dict,
         # Route structlog through stdlib so all output shares one handler.
         logger_factory=structlog.stdlib.LoggerFactory(),
-        cache_logger_on_first_use=True,
+        cache_logger_on_first_use=False,
     )
 
     # ProcessorFormatter ensures uvicorn access logs and every other stdlib
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.59s
operator: core/ReplaceTrueWithFalse, occurrence: 3
--- mutation diff ---
--- astampbot/logger.py
+++ bstampbot/logger.py
@@ -101,7 +101,7 @@
 
     # Instrument logging with OpenTelemetry if enabled
     if settings.otel_enabled:
-        LoggingInstrumentor().instrument(set_logging_format=True)
+        LoggingInstrumentor().instrument(set_logging_format=False)
 
 
 def get_logger(name: str) -> structlog.BoundLogger:
........................................................................ [ 34%]
................F
=================================== FAILURES ===================================
___________________ test_configure_logging_with_otel_enabled ___________________

    def test_configure_logging_with_otel_enabled():
        """Test configure_logging instruments logging when OTEL is enabled."""
        with (
            patch("stampbot.logger.settings") as mock_settings,
            patch("stampbot.logger.LoggingInstrumentor") as mock_instrumentor,
        ):
            mock_settings.log_format = "json"
            mock_settings.log_level = "INFO"
            mock_settings.otel_enabled = True
    
            from stampbot.logger import configure_logging
    
            configure_logging()
    
            # Verify LoggingInstrumentor was called
>           mock_instrumentor.return_value.instrument.assert_called_once_with(set_logging_format=True)

tests/test_logger.py:104: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:997: in assert_called_once_with
    return self.assert_called_with(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <MagicMock name='LoggingInstrumentor().instrument' id='140382584923072'>
args = (), kwargs = {'set_logging_format': True}
expected = call(set_logging_format=True)
actual = call(set_logging_format=False)
_error_message = <function NonCallableMock.assert_called_with.<locals>._error_message at 0x7fad5e1e8300>
cause = None

    def assert_called_with(self, /, *args, **kwargs):
        """assert that the last call was made with the specified arguments.
    
        Raises an AssertionError if the args and keyword args passed in are
        different to the last call to the mock."""
        if self.call_args is None:
            expected = self._format_mock_call_signature(args, kwargs)
            actual = 'not called.'
            error_message = ('expected call not found.\nExpected: %s\n  Actual: %s'
                    % (expected, actual))
            raise AssertionError(error_message)
    
        def _error_message():
            msg = self._format_mock_failure_message(args, kwargs)
            return msg
        expected = self._call_matcher(_Call((args, kwargs), two=True))
        actual = self._call_matcher(self.call_args)
        if actual != expected:
            cause = expected if isinstance(expected, Exception) else None
>           raise AssertionError(_error_message()) from cause
E           AssertionError: expected call not found.
E           Expected: instrument(set_logging_format=True)
E             Actual: instrument(set_logging_format=False)

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:985: AssertionError
=========================== short test summary info ============================
FAILED tests/test_logger.py::test_configure_logging_with_otel_enabled - AssertionError: expected call not found.
Expected: instrument(set_logging_format=True)
  Actual: instrument(set_logging_format=False)
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 88 passed, 3 deselected in 0.85s
operator: core/ReplaceBinaryOperator_Sub_Add, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -168,7 +168,7 @@
                     retry=retry,
                 )
 
-                duration = time.time() - start_time
+                duration = time.time() + start_time
                 github_api_request_duration_seconds.labels(operation="get_token").observe(duration)
                 github_api_requests_total.labels(operation="get_token", status="success").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Add, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -176,7 +176,7 @@
                 return client
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() + start_time
                 github_api_request_duration_seconds.labels(operation="get_token").observe(duration)
                 github_api_requests_total.labels(operation="get_token", status="failure").inc()
                 set_span_error(span, e)
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Add, occurrence: 2
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -240,7 +240,7 @@
                     event="APPROVE",
                 )
 
-                duration = time.time() - start_time
+                duration = time.time() + start_time
                 github_api_request_duration_seconds.labels(operation="approve").observe(duration)
                 github_api_requests_total.labels(operation="approve", status="success").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Add, occurrence: 3
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -260,7 +260,7 @@
                 return True
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() + start_time
                 github_api_request_duration_seconds.labels(operation="approve").observe(duration)
                 github_api_requests_total.labels(operation="approve", status="failure").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_Add, occurrence: 4
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -317,7 +317,7 @@
                 review = pr.get_review(review_id)
                 review.dismiss(message)
 
-                duration = time.time() - start_time
+                duration = time.time() + start_time
                 github_api_request_duration_seconds.labels(operation="dismiss").observe(duration)
                 github_api_requests_total.labels(operation="dismiss", status="success").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Add, occurrence: 5
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -338,7 +338,7 @@
                 return True
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() + start_time
                 github_api_request_duration_seconds.labels(operation="dismiss").observe(duration)
                 github_api_requests_total.labels(operation="dismiss", status="failure").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Add, occurrence: 6
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -393,7 +393,7 @@
 
                 content = repo.get_contents(file_path, ref=ref)  # type: ignore[arg-type]
                 if isinstance(content, list):
-                    duration = time.time() - start_time
+                    duration = time.time() + start_time
                     github_api_request_duration_seconds.labels(operation="get_file").observe(
                         duration
                     )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Add, occurrence: 7
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -402,7 +402,7 @@
                     set_span_ok(span)
                     return None
 
-                duration = time.time() - start_time
+                duration = time.time() + start_time
                 github_api_request_duration_seconds.labels(operation="get_file").observe(duration)
                 github_api_requests_total.labels(operation="get_file", status="success").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Add, occurrence: 8
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -414,7 +414,7 @@
                 return content.decoded_content.decode("utf-8")
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() + start_time
                 github_api_request_duration_seconds.labels(operation="get_file").observe(duration)
                 github_api_requests_total.labels(operation="get_file", status="not_found").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_Add, occurrence: 9
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -473,7 +473,7 @@
                     if review.user.login == bot_user and review.state == "APPROVED":
                         bot_review_ids.append(review.id)
 
-                duration = time.time() - start_time
+                duration = time.time() + start_time
                 github_api_request_duration_seconds.labels(operation="find_reviews").observe(
                     duration
                 )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Add, occurrence: 10
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -489,7 +489,7 @@
                 return bot_review_ids
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() + start_time
                 github_api_request_duration_seconds.labels(operation="find_reviews").observe(
                     duration
                 )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Add, occurrence: 11
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -546,7 +546,7 @@
 
                 pr.create_review(body=message, event="COMMENT")
 
-                duration = time.time() - start_time
+                duration = time.time() + start_time
                 github_api_request_duration_seconds.labels(operation="comment").observe(duration)
                 github_api_requests_total.labels(operation="comment", status="success").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Add, occurrence: 12
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -568,7 +568,7 @@
                 return True
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() + start_time
                 github_api_request_duration_seconds.labels(operation="comment").observe(duration)
                 github_api_requests_total.labels(operation="comment", status="failure").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Add, occurrence: 13
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -619,7 +619,7 @@
                 repo = client.get_repo(repo_full_name)
                 repo.get_label(label_name)
 
-                duration = time.time() - start_time
+                duration = time.time() + start_time
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="success").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_Add, occurrence: 14
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -630,7 +630,7 @@
                 return True
 
             except GithubException as e:
-                duration = time.time() - start_time
+                duration = time.time() + start_time
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="failure").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceBinaryOperator_Sub_Add, occurrence: 15
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -655,7 +655,7 @@
                 return None
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() + start_time
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="failure").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceBinaryOperator_Sub_Add, occurrence: 16
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -718,7 +718,7 @@
 
                 has_permission = permission_index >= required_index
 
-                duration = time.time() - start_time
+                duration = time.time() + start_time
                 github_api_request_duration_seconds.labels(operation="get_permission").observe(
                     duration
                 )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Add, occurrence: 17
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -738,7 +738,7 @@
                 return has_permission
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() + start_time
                 github_api_request_duration_seconds.labels(operation="get_permission").observe(
                     duration
                 )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Add, occurrence: 18
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -829,7 +829,7 @@
                         )
                         continue
 
-                duration = time.time() - start_time
+                duration = time.time() + start_time
                 github_api_request_duration_seconds.labels(operation="get_user_teams").observe(
                     duration
                 )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_Add, occurrence: 19
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -849,7 +849,7 @@
                 return member_teams
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() + start_time
                 github_api_request_duration_seconds.labels(operation="get_user_teams").observe(
                     duration
                 )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Mul, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -168,7 +168,7 @@
                     retry=retry,
                 )
 
-                duration = time.time() - start_time
+                duration = time.time() * start_time
                 github_api_request_duration_seconds.labels(operation="get_token").observe(duration)
                 github_api_requests_total.labels(operation="get_token", status="success").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_Mul, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -176,7 +176,7 @@
                 return client
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() * start_time
                 github_api_request_duration_seconds.labels(operation="get_token").observe(duration)
                 github_api_requests_total.labels(operation="get_token", status="failure").inc()
                 set_span_error(span, e)
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_Mul, occurrence: 2
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -240,7 +240,7 @@
                     event="APPROVE",
                 )
 
-                duration = time.time() - start_time
+                duration = time.time() * start_time
                 github_api_request_duration_seconds.labels(operation="approve").observe(duration)
                 github_api_requests_total.labels(operation="approve", status="success").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceBinaryOperator_Sub_Mul, occurrence: 3
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -260,7 +260,7 @@
                 return True
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() * start_time
                 github_api_request_duration_seconds.labels(operation="approve").observe(duration)
                 github_api_requests_total.labels(operation="approve", status="failure").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_Mul, occurrence: 4
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -317,7 +317,7 @@
                 review = pr.get_review(review_id)
                 review.dismiss(message)
 
-                duration = time.time() - start_time
+                duration = time.time() * start_time
                 github_api_request_duration_seconds.labels(operation="dismiss").observe(duration)
                 github_api_requests_total.labels(operation="dismiss", status="success").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_Mul, occurrence: 5
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -338,7 +338,7 @@
                 return True
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() * start_time
                 github_api_request_duration_seconds.labels(operation="dismiss").observe(duration)
                 github_api_requests_total.labels(operation="dismiss", status="failure").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Mul, occurrence: 6
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -393,7 +393,7 @@
 
                 content = repo.get_contents(file_path, ref=ref)  # type: ignore[arg-type]
                 if isinstance(content, list):
-                    duration = time.time() - start_time
+                    duration = time.time() * start_time
                     github_api_request_duration_seconds.labels(operation="get_file").observe(
                         duration
                     )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_Mul, occurrence: 7
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -402,7 +402,7 @@
                     set_span_ok(span)
                     return None
 
-                duration = time.time() - start_time
+                duration = time.time() * start_time
                 github_api_request_duration_seconds.labels(operation="get_file").observe(duration)
                 github_api_requests_total.labels(operation="get_file", status="success").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_Mul, occurrence: 8
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -414,7 +414,7 @@
                 return content.decoded_content.decode("utf-8")
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() * start_time
                 github_api_request_duration_seconds.labels(operation="get_file").observe(duration)
                 github_api_requests_total.labels(operation="get_file", status="not_found").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Mul, occurrence: 9
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -473,7 +473,7 @@
                     if review.user.login == bot_user and review.state == "APPROVED":
                         bot_review_ids.append(review.id)
 
-                duration = time.time() - start_time
+                duration = time.time() * start_time
                 github_api_request_duration_seconds.labels(operation="find_reviews").observe(
                     duration
                 )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Mul, occurrence: 10
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -489,7 +489,7 @@
                 return bot_review_ids
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() * start_time
                 github_api_request_duration_seconds.labels(operation="find_reviews").observe(
                     duration
                 )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceBinaryOperator_Sub_Mul, occurrence: 11
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -546,7 +546,7 @@
 
                 pr.create_review(body=message, event="COMMENT")
 
-                duration = time.time() - start_time
+                duration = time.time() * start_time
                 github_api_request_duration_seconds.labels(operation="comment").observe(duration)
                 github_api_requests_total.labels(operation="comment", status="success").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_Mul, occurrence: 12
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -568,7 +568,7 @@
                 return True
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() * start_time
                 github_api_request_duration_seconds.labels(operation="comment").observe(duration)
                 github_api_requests_total.labels(operation="comment", status="failure").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.64s
operator: core/ReplaceBinaryOperator_Sub_Mul, occurrence: 13
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -619,7 +619,7 @@
                 repo = client.get_repo(repo_full_name)
                 repo.get_label(label_name)
 
-                duration = time.time() - start_time
+                duration = time.time() * start_time
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="success").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_Mul, occurrence: 14
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -630,7 +630,7 @@
                 return True
 
             except GithubException as e:
-                duration = time.time() - start_time
+                duration = time.time() * start_time
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="failure").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Mul, occurrence: 15
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -655,7 +655,7 @@
                 return None
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() * start_time
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="failure").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Mul, occurrence: 16
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -718,7 +718,7 @@
 
                 has_permission = permission_index >= required_index
 
-                duration = time.time() - start_time
+                duration = time.time() * start_time
                 github_api_request_duration_seconds.labels(operation="get_permission").observe(
                     duration
                 )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Mul, occurrence: 17
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -738,7 +738,7 @@
                 return has_permission
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() * start_time
                 github_api_request_duration_seconds.labels(operation="get_permission").observe(
                     duration
                 )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_Mul, occurrence: 18
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -829,7 +829,7 @@
                         )
                         continue
 
-                duration = time.time() - start_time
+                duration = time.time() * start_time
                 github_api_request_duration_seconds.labels(operation="get_user_teams").observe(
                     duration
                 )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Mul, occurrence: 19
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -849,7 +849,7 @@
                 return member_teams
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() * start_time
                 github_api_request_duration_seconds.labels(operation="get_user_teams").observe(
                     duration
                 )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceBinaryOperator_Sub_Div, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -168,7 +168,7 @@
                     retry=retry,
                 )
 
-                duration = time.time() - start_time
+                duration = time.time() / start_time
                 github_api_request_duration_seconds.labels(operation="get_token").observe(duration)
                 github_api_requests_total.labels(operation="get_token", status="success").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceBinaryOperator_Sub_Div, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -176,7 +176,7 @@
                 return client
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() / start_time
                 github_api_request_duration_seconds.labels(operation="get_token").observe(duration)
                 github_api_requests_total.labels(operation="get_token", status="failure").inc()
                 set_span_error(span, e)
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Div, occurrence: 2
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -240,7 +240,7 @@
                     event="APPROVE",
                 )
 
-                duration = time.time() - start_time
+                duration = time.time() / start_time
                 github_api_request_duration_seconds.labels(operation="approve").observe(duration)
                 github_api_requests_total.labels(operation="approve", status="success").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Div, occurrence: 3
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -260,7 +260,7 @@
                 return True
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() / start_time
                 github_api_request_duration_seconds.labels(operation="approve").observe(duration)
                 github_api_requests_total.labels(operation="approve", status="failure").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_Div, occurrence: 4
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -317,7 +317,7 @@
                 review = pr.get_review(review_id)
                 review.dismiss(message)
 
-                duration = time.time() - start_time
+                duration = time.time() / start_time
                 github_api_request_duration_seconds.labels(operation="dismiss").observe(duration)
                 github_api_requests_total.labels(operation="dismiss", status="success").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Div, occurrence: 5
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -338,7 +338,7 @@
                 return True
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() / start_time
                 github_api_request_duration_seconds.labels(operation="dismiss").observe(duration)
                 github_api_requests_total.labels(operation="dismiss", status="failure").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_Div, occurrence: 6
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -393,7 +393,7 @@
 
                 content = repo.get_contents(file_path, ref=ref)  # type: ignore[arg-type]
                 if isinstance(content, list):
-                    duration = time.time() - start_time
+                    duration = time.time() / start_time
                     github_api_request_duration_seconds.labels(operation="get_file").observe(
                         duration
                     )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.64s
operator: core/ReplaceBinaryOperator_Sub_Div, occurrence: 7
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -402,7 +402,7 @@
                     set_span_ok(span)
                     return None
 
-                duration = time.time() - start_time
+                duration = time.time() / start_time
                 github_api_request_duration_seconds.labels(operation="get_file").observe(duration)
                 github_api_requests_total.labels(operation="get_file", status="success").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceBinaryOperator_Sub_Div, occurrence: 8
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -414,7 +414,7 @@
                 return content.decoded_content.decode("utf-8")
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() / start_time
                 github_api_request_duration_seconds.labels(operation="get_file").observe(duration)
                 github_api_requests_total.labels(operation="get_file", status="not_found").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/ReplaceBinaryOperator_Sub_Div, occurrence: 9
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -473,7 +473,7 @@
                     if review.user.login == bot_user and review.state == "APPROVED":
                         bot_review_ids.append(review.id)
 
-                duration = time.time() - start_time
+                duration = time.time() / start_time
                 github_api_request_duration_seconds.labels(operation="find_reviews").observe(
                     duration
                 )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Div, occurrence: 10
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -489,7 +489,7 @@
                 return bot_review_ids
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() / start_time
                 github_api_request_duration_seconds.labels(operation="find_reviews").observe(
                     duration
                 )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.64s
operator: core/ReplaceBinaryOperator_Sub_Div, occurrence: 11
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -546,7 +546,7 @@
 
                 pr.create_review(body=message, event="COMMENT")
 
-                duration = time.time() - start_time
+                duration = time.time() / start_time
                 github_api_request_duration_seconds.labels(operation="comment").observe(duration)
                 github_api_requests_total.labels(operation="comment", status="success").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_Div, occurrence: 12
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -568,7 +568,7 @@
                 return True
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() / start_time
                 github_api_request_duration_seconds.labels(operation="comment").observe(duration)
                 github_api_requests_total.labels(operation="comment", status="failure").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Div, occurrence: 13
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -619,7 +619,7 @@
                 repo = client.get_repo(repo_full_name)
                 repo.get_label(label_name)
 
-                duration = time.time() - start_time
+                duration = time.time() / start_time
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="success").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_Div, occurrence: 14
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -630,7 +630,7 @@
                 return True
 
             except GithubException as e:
-                duration = time.time() - start_time
+                duration = time.time() / start_time
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="failure").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_Div, occurrence: 15
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -655,7 +655,7 @@
                 return None
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() / start_time
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="failure").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceBinaryOperator_Sub_Div, occurrence: 16
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -718,7 +718,7 @@
 
                 has_permission = permission_index >= required_index
 
-                duration = time.time() - start_time
+                duration = time.time() / start_time
                 github_api_request_duration_seconds.labels(operation="get_permission").observe(
                     duration
                 )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Div, occurrence: 17
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -738,7 +738,7 @@
                 return has_permission
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() / start_time
                 github_api_request_duration_seconds.labels(operation="get_permission").observe(
                     duration
                 )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/ReplaceBinaryOperator_Sub_Div, occurrence: 18
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -829,7 +829,7 @@
                         )
                         continue
 
-                duration = time.time() - start_time
+                duration = time.time() / start_time
                 github_api_request_duration_seconds.labels(operation="get_user_teams").observe(
                     duration
                 )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_Div, occurrence: 19
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -849,7 +849,7 @@
                 return member_teams
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() / start_time
                 github_api_request_duration_seconds.labels(operation="get_user_teams").observe(
                     duration
                 )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_FloorDiv, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -168,7 +168,7 @@
                     retry=retry,
                 )
 
-                duration = time.time() - start_time
+                duration = time.time() // start_time
                 github_api_request_duration_seconds.labels(operation="get_token").observe(duration)
                 github_api_requests_total.labels(operation="get_token", status="success").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_FloorDiv, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -176,7 +176,7 @@
                 return client
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() // start_time
                 github_api_request_duration_seconds.labels(operation="get_token").observe(duration)
                 github_api_requests_total.labels(operation="get_token", status="failure").inc()
                 set_span_error(span, e)
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/ReplaceBinaryOperator_Sub_FloorDiv, occurrence: 2
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -240,7 +240,7 @@
                     event="APPROVE",
                 )
 
-                duration = time.time() - start_time
+                duration = time.time() // start_time
                 github_api_request_duration_seconds.labels(operation="approve").observe(duration)
                 github_api_requests_total.labels(operation="approve", status="success").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_FloorDiv, occurrence: 3
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -260,7 +260,7 @@
                 return True
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() // start_time
                 github_api_request_duration_seconds.labels(operation="approve").observe(duration)
                 github_api_requests_total.labels(operation="approve", status="failure").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_FloorDiv, occurrence: 4
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -317,7 +317,7 @@
                 review = pr.get_review(review_id)
                 review.dismiss(message)
 
-                duration = time.time() - start_time
+                duration = time.time() // start_time
                 github_api_request_duration_seconds.labels(operation="dismiss").observe(duration)
                 github_api_requests_total.labels(operation="dismiss", status="success").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceBinaryOperator_Sub_FloorDiv, occurrence: 5
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -338,7 +338,7 @@
                 return True
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() // start_time
                 github_api_request_duration_seconds.labels(operation="dismiss").observe(duration)
                 github_api_requests_total.labels(operation="dismiss", status="failure").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_FloorDiv, occurrence: 6
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -393,7 +393,7 @@
 
                 content = repo.get_contents(file_path, ref=ref)  # type: ignore[arg-type]
                 if isinstance(content, list):
-                    duration = time.time() - start_time
+                    duration = time.time() // start_time
                     github_api_request_duration_seconds.labels(operation="get_file").observe(
                         duration
                     )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceBinaryOperator_Sub_FloorDiv, occurrence: 7
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -402,7 +402,7 @@
                     set_span_ok(span)
                     return None
 
-                duration = time.time() - start_time
+                duration = time.time() // start_time
                 github_api_request_duration_seconds.labels(operation="get_file").observe(duration)
                 github_api_requests_total.labels(operation="get_file", status="success").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_FloorDiv, occurrence: 8
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -414,7 +414,7 @@
                 return content.decoded_content.decode("utf-8")
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() // start_time
                 github_api_request_duration_seconds.labels(operation="get_file").observe(duration)
                 github_api_requests_total.labels(operation="get_file", status="not_found").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_FloorDiv, occurrence: 9
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -473,7 +473,7 @@
                     if review.user.login == bot_user and review.state == "APPROVED":
                         bot_review_ids.append(review.id)
 
-                duration = time.time() - start_time
+                duration = time.time() // start_time
                 github_api_request_duration_seconds.labels(operation="find_reviews").observe(
                     duration
                 )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_FloorDiv, occurrence: 10
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -489,7 +489,7 @@
                 return bot_review_ids
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() // start_time
                 github_api_request_duration_seconds.labels(operation="find_reviews").observe(
                     duration
                 )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/ReplaceBinaryOperator_Sub_FloorDiv, occurrence: 11
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -546,7 +546,7 @@
 
                 pr.create_review(body=message, event="COMMENT")
 
-                duration = time.time() - start_time
+                duration = time.time() // start_time
                 github_api_request_duration_seconds.labels(operation="comment").observe(duration)
                 github_api_requests_total.labels(operation="comment", status="success").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_FloorDiv, occurrence: 12
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -568,7 +568,7 @@
                 return True
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() // start_time
                 github_api_request_duration_seconds.labels(operation="comment").observe(duration)
                 github_api_requests_total.labels(operation="comment", status="failure").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_FloorDiv, occurrence: 13
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -619,7 +619,7 @@
                 repo = client.get_repo(repo_full_name)
                 repo.get_label(label_name)
 
-                duration = time.time() - start_time
+                duration = time.time() // start_time
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="success").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_FloorDiv, occurrence: 14
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -630,7 +630,7 @@
                 return True
 
             except GithubException as e:
-                duration = time.time() - start_time
+                duration = time.time() // start_time
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="failure").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_FloorDiv, occurrence: 15
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -655,7 +655,7 @@
                 return None
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() // start_time
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="failure").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.64s
operator: core/ReplaceBinaryOperator_Sub_FloorDiv, occurrence: 16
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -718,7 +718,7 @@
 
                 has_permission = permission_index >= required_index
 
-                duration = time.time() - start_time
+                duration = time.time() // start_time
                 github_api_request_duration_seconds.labels(operation="get_permission").observe(
                     duration
                 )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_FloorDiv, occurrence: 17
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -738,7 +738,7 @@
                 return has_permission
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() // start_time
                 github_api_request_duration_seconds.labels(operation="get_permission").observe(
                     duration
                 )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_FloorDiv, occurrence: 18
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -829,7 +829,7 @@
                         )
                         continue
 
-                duration = time.time() - start_time
+                duration = time.time() // start_time
                 github_api_request_duration_seconds.labels(operation="get_user_teams").observe(
                     duration
                 )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/ReplaceBinaryOperator_Sub_FloorDiv, occurrence: 19
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -849,7 +849,7 @@
                 return member_teams
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() // start_time
                 github_api_request_duration_seconds.labels(operation="get_user_teams").observe(
                     duration
                 )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_Mod, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -168,7 +168,7 @@
                     retry=retry,
                 )
 
-                duration = time.time() - start_time
+                duration = time.time() % start_time
                 github_api_request_duration_seconds.labels(operation="get_token").observe(duration)
                 github_api_requests_total.labels(operation="get_token", status="success").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Mod, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -176,7 +176,7 @@
                 return client
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() % start_time
                 github_api_request_duration_seconds.labels(operation="get_token").observe(duration)
                 github_api_requests_total.labels(operation="get_token", status="failure").inc()
                 set_span_error(span, e)
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Mod, occurrence: 2
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -240,7 +240,7 @@
                     event="APPROVE",
                 )
 
-                duration = time.time() - start_time
+                duration = time.time() % start_time
                 github_api_request_duration_seconds.labels(operation="approve").observe(duration)
                 github_api_requests_total.labels(operation="approve", status="success").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Mod, occurrence: 3
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -260,7 +260,7 @@
                 return True
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() % start_time
                 github_api_request_duration_seconds.labels(operation="approve").observe(duration)
                 github_api_requests_total.labels(operation="approve", status="failure").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Mod, occurrence: 4
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -317,7 +317,7 @@
                 review = pr.get_review(review_id)
                 review.dismiss(message)
 
-                duration = time.time() - start_time
+                duration = time.time() % start_time
                 github_api_request_duration_seconds.labels(operation="dismiss").observe(duration)
                 github_api_requests_total.labels(operation="dismiss", status="success").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/ReplaceBinaryOperator_Sub_Mod, occurrence: 5
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -338,7 +338,7 @@
                 return True
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() % start_time
                 github_api_request_duration_seconds.labels(operation="dismiss").observe(duration)
                 github_api_requests_total.labels(operation="dismiss", status="failure").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Mod, occurrence: 6
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -393,7 +393,7 @@
 
                 content = repo.get_contents(file_path, ref=ref)  # type: ignore[arg-type]
                 if isinstance(content, list):
-                    duration = time.time() - start_time
+                    duration = time.time() % start_time
                     github_api_request_duration_seconds.labels(operation="get_file").observe(
                         duration
                     )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.65s
operator: core/ReplaceBinaryOperator_Sub_Mod, occurrence: 7
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -402,7 +402,7 @@
                     set_span_ok(span)
                     return None
 
-                duration = time.time() - start_time
+                duration = time.time() % start_time
                 github_api_request_duration_seconds.labels(operation="get_file").observe(duration)
                 github_api_requests_total.labels(operation="get_file", status="success").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Mod, occurrence: 8
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -414,7 +414,7 @@
                 return content.decoded_content.decode("utf-8")
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() % start_time
                 github_api_request_duration_seconds.labels(operation="get_file").observe(duration)
                 github_api_requests_total.labels(operation="get_file", status="not_found").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_Mod, occurrence: 9
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -473,7 +473,7 @@
                     if review.user.login == bot_user and review.state == "APPROVED":
                         bot_review_ids.append(review.id)
 
-                duration = time.time() - start_time
+                duration = time.time() % start_time
                 github_api_request_duration_seconds.labels(operation="find_reviews").observe(
                     duration
                 )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Mod, occurrence: 10
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -489,7 +489,7 @@
                 return bot_review_ids
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() % start_time
                 github_api_request_duration_seconds.labels(operation="find_reviews").observe(
                     duration
                 )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_Mod, occurrence: 11
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -546,7 +546,7 @@
 
                 pr.create_review(body=message, event="COMMENT")
 
-                duration = time.time() - start_time
+                duration = time.time() % start_time
                 github_api_request_duration_seconds.labels(operation="comment").observe(duration)
                 github_api_requests_total.labels(operation="comment", status="success").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_Mod, occurrence: 12
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -568,7 +568,7 @@
                 return True
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() % start_time
                 github_api_request_duration_seconds.labels(operation="comment").observe(duration)
                 github_api_requests_total.labels(operation="comment", status="failure").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_Mod, occurrence: 13
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -619,7 +619,7 @@
                 repo = client.get_repo(repo_full_name)
                 repo.get_label(label_name)
 
-                duration = time.time() - start_time
+                duration = time.time() % start_time
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="success").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Mod, occurrence: 14
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -630,7 +630,7 @@
                 return True
 
             except GithubException as e:
-                duration = time.time() - start_time
+                duration = time.time() % start_time
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="failure").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/ReplaceBinaryOperator_Sub_Mod, occurrence: 15
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -655,7 +655,7 @@
                 return None
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() % start_time
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="failure").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_Mod, occurrence: 16
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -718,7 +718,7 @@
 
                 has_permission = permission_index >= required_index
 
-                duration = time.time() - start_time
+                duration = time.time() % start_time
                 github_api_request_duration_seconds.labels(operation="get_permission").observe(
                     duration
                 )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Mod, occurrence: 17
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -738,7 +738,7 @@
                 return has_permission
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() % start_time
                 github_api_request_duration_seconds.labels(operation="get_permission").observe(
                     duration
                 )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_Mod, occurrence: 18
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -829,7 +829,7 @@
                         )
                         continue
 
-                duration = time.time() - start_time
+                duration = time.time() % start_time
                 github_api_request_duration_seconds.labels(operation="get_user_teams").observe(
                     duration
                 )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_Mod, occurrence: 19
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -849,7 +849,7 @@
                 return member_teams
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() % start_time
                 github_api_request_duration_seconds.labels(operation="get_user_teams").observe(
                     duration
                 )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Pow, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -168,7 +168,7 @@
                     retry=retry,
                 )
 
-                duration = time.time() - start_time
+                duration = time.time() ** start_time
                 github_api_request_duration_seconds.labels(operation="get_token").observe(duration)
                 github_api_requests_total.labels(operation="get_token", status="success").inc()
 
...............................................F
=================================== FAILURES ===================================
________ TestGetInstallationClient.test_get_installation_client_success ________

self = <tests.test_github_client.TestGetInstallationClient object at 0x7faa34358050>

    def test_get_installation_client_success(self):
        """Test successful installation client creation."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client._get_installation_client(123456)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:211: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7faa304a8830>
installation_id = 123456

    def _get_installation_client(self, installation_id: int) -> Github:
        """Get authenticated GitHub client for an installation.
    
        Args:
            installation_id: GitHub App installation ID.
    
        Returns:
            Authenticated Github client instance with timeout and retry configured.
    
        Raises:
            github.GithubException: If token exchange fails.
        """
        start_time = time.time()
    
        with create_span(
            "github.get_installation_token",
            {"github.installation_id": installation_id},
        ) as span:
            try:
                auth = self.integration.get_access_token(installation_id)
    
                # Configure retry with exponential backoff
                retry = Retry(
                    total=GITHUB_API_RETRY_TOTAL,
                    backoff_factor=GITHUB_API_RETRY_BACKOFF,
                    status_forcelist=[500, 502, 503, 504],
                    allowed_methods=["GET", "POST", "PUT", "DELETE", "PATCH"],
                )
    
                client = Github(
                    auth=Auth.Token(auth.token),
                    timeout=GITHUB_API_TIMEOUT,
                    retry=retry,
                )
    
>               duration = time.time() ** start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^^
E               OverflowError: (34, 'Numerical result out of range')

stampbot/github_client.py:171: OverflowError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetInstallationClient::test_get_installation_client_success - OverflowError: (34, 'Numerical result out of range')
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 47 passed, 3 deselected in 0.76s
operator: core/ReplaceBinaryOperator_Sub_Pow, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -176,7 +176,7 @@
                 return client
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() ** start_time
                 github_api_request_duration_seconds.labels(operation="get_token").observe(duration)
                 github_api_requests_total.labels(operation="get_token", status="failure").inc()
                 set_span_error(span, e)
................................................F
=================================== FAILURES ===================================
________ TestGetInstallationClient.test_get_installation_client_failure ________

self = <stampbot.github_client.GitHubAppClient object at 0x7f68379c9230>
installation_id = 123456

    def _get_installation_client(self, installation_id: int) -> Github:
        """Get authenticated GitHub client for an installation.
    
        Args:
            installation_id: GitHub App installation ID.
    
        Returns:
            Authenticated Github client instance with timeout and retry configured.
    
        Raises:
            github.GithubException: If token exchange fails.
        """
        start_time = time.time()
    
        with create_span(
            "github.get_installation_token",
            {"github.installation_id": installation_id},
        ) as span:
            try:
>               auth = self.integration.get_access_token(installation_id)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:155: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='GithubIntegration().get_access_token' id='140085566946816'>
args = (123456,), kwargs = {}, effect = Exception('Token exchange failed')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: Token exchange failed

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestGetInstallationClient object at 0x7f683a7ac190>

    def test_get_installation_client_failure(self):
        """Test installation client creation failure."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_integration.get_access_token.side_effect = Exception("Token exchange failed")
            mock_integration_cls.return_value = mock_integration
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            with pytest.raises(Exception, match="Token exchange failed"):
>               client._get_installation_client(123456)

tests/test_github_client.py:240: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f68379c9230>
installation_id = 123456

    def _get_installation_client(self, installation_id: int) -> Github:
        """Get authenticated GitHub client for an installation.
    
        Args:
            installation_id: GitHub App installation ID.
    
        Returns:
            Authenticated Github client instance with timeout and retry configured.
    
        Raises:
            github.GithubException: If token exchange fails.
        """
        start_time = time.time()
    
        with create_span(
            "github.get_installation_token",
            {"github.installation_id": installation_id},
        ) as span:
            try:
                auth = self.integration.get_access_token(installation_id)
    
                # Configure retry with exponential backoff
                retry = Retry(
                    total=GITHUB_API_RETRY_TOTAL,
                    backoff_factor=GITHUB_API_RETRY_BACKOFF,
                    status_forcelist=[500, 502, 503, 504],
                    allowed_methods=["GET", "POST", "PUT", "DELETE", "PATCH"],
                )
    
                client = Github(
                    auth=Auth.Token(auth.token),
                    timeout=GITHUB_API_TIMEOUT,
                    retry=retry,
                )
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_token").observe(duration)
                github_api_requests_total.labels(operation="get_token", status="success").inc()
    
                set_span_ok(span)
                return client
    
            except Exception as e:
>               duration = time.time() ** start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^^
E               OverflowError: (34, 'Numerical result out of range')

stampbot/github_client.py:179: OverflowError

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestGetInstallationClient object at 0x7f683a7ac190>

    def test_get_installation_client_failure(self):
        """Test installation client creation failure."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_integration.get_access_token.side_effect = Exception("Token exchange failed")
            mock_integration_cls.return_value = mock_integration
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           with pytest.raises(Exception, match="Token exchange failed"):
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E           AssertionError: Regex pattern did not match.
E             Expected regex: 'Token exchange failed'
E             Actual message: "(34, 'Numerical result out of range')"

tests/test_github_client.py:239: AssertionError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetInstallationClient::test_get_installation_client_failure - AssertionError: Regex pattern did not match.
  Expected regex: 'Token exchange failed'
  Actual message: "(34, 'Numerical result out of range')"
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 48 passed, 3 deselected in 0.88s
operator: core/ReplaceBinaryOperator_Sub_Pow, occurrence: 2
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -240,7 +240,7 @@
                     event="APPROVE",
                 )
 
-                duration = time.time() - start_time
+                duration = time.time() ** start_time
                 github_api_request_duration_seconds.labels(operation="approve").observe(duration)
                 github_api_requests_total.labels(operation="approve", status="success").inc()
 
...................................................F
=================================== FAILURES ===================================
____________________ TestApprovePR.test_approve_pr_success _____________________

self = <tests.test_github_client.TestApprovePR object at 0x7fc0cd34c550>

    def test_approve_pr_success(self):
        """Test successful PR approval."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_pr = Mock()
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.approve_pr(123456, "owner/repo", 42)
    
>           assert result is True
E           assert False is True

tests/test_github_client.py:313: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 14:08:37 [error    ] Failed to approve PR #42 in owner/repo: (34, 'Numerical result out of range') extra={'repo': 'owner/repo', 'pr_number': 42, 'installation_id': 123456, 'error': "(34, 'Numerical result out of range')"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestApprovePR::test_approve_pr_success - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 51 passed, 3 deselected in 0.75s
operator: core/ReplaceBinaryOperator_Sub_Pow, occurrence: 3
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -260,7 +260,7 @@
                 return True
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() ** start_time
                 github_api_request_duration_seconds.labels(operation="approve").observe(duration)
                 github_api_requests_total.labels(operation="approve", status="failure").inc()
 
....................................................F
=================================== FAILURES ===================================
____________________ TestApprovePR.test_approve_pr_failure _____________________

self = <stampbot.github_client.GitHubAppClient object at 0x7f05ed066c50>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
comment = 'Auto-approved by Stampbot'

    def approve_pr(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        comment: str = "Auto-approved by Stampbot",
    ) -> bool:
        """Approve a pull request.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            comment: Review comment
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.approve_pr",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                # Create approval review
>               pr.create_review(
                    body=comment,
                    event="APPROVE",
                )

stampbot/github_client.py:238: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_pull().create_review' id='139663428581568'>
args = (), kwargs = {'body': 'Auto-approved by Stampbot', 'event': 'APPROVE'}
effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestApprovePR object at 0x7f05f1450690>

    def test_approve_pr_failure(self):
        """Test PR approval failure."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_pr = Mock()
            mock_pr.create_review.side_effect = Exception("API Error")
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.approve_pr(123456, "owner/repo", 42)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:350: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f05ed066c50>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
comment = 'Auto-approved by Stampbot'

    def approve_pr(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        comment: str = "Auto-approved by Stampbot",
    ) -> bool:
        """Approve a pull request.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            comment: Review comment
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.approve_pr",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                # Create approval review
                pr.create_review(
                    body=comment,
                    event="APPROVE",
                )
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="approve").observe(duration)
                github_api_requests_total.labels(operation="approve", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.result": "approved"})
                set_span_ok(span)
    
                logger.info(
                    f"Approved PR #{pr_number} in {repo_full_name}",
                    extra={
                        "repo": repo_full_name,
                        "pr_number": pr_number,
                        "installation_id": installation_id,
                    },
                )
                return True
    
            except Exception as e:
>               duration = time.time() ** start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^^
E               OverflowError: (34, 'Numerical result out of range')

stampbot/github_client.py:263: OverflowError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestApprovePR::test_approve_pr_failure - OverflowError: (34, 'Numerical result out of range')
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 52 passed, 3 deselected in 0.86s
operator: core/ReplaceBinaryOperator_Sub_Pow, occurrence: 4
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -317,7 +317,7 @@
                 review = pr.get_review(review_id)
                 review.dismiss(message)
 
-                duration = time.time() - start_time
+                duration = time.time() ** start_time
                 github_api_request_duration_seconds.labels(operation="dismiss").observe(duration)
                 github_api_requests_total.labels(operation="dismiss", status="success").inc()
 
.....................................................F
=================================== FAILURES ===================================
______________ TestDismissApproval.test_dismiss_approval_success _______________

self = <tests.test_github_client.TestDismissApproval object at 0x7fe08b2f87d0>

    def test_dismiss_approval_success(self):
        """Test successful approval dismissal."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_review = Mock()
            mock_pr = Mock()
            mock_pr.get_review.return_value = mock_review
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.dismiss_approval(123456, "owner/repo", 42, 999)
    
>           assert result is True
E           assert False is True

tests/test_github_client.py:396: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:57:28 [error    ] Failed to dismiss approval for PR #42 in owner/repo: (34, 'Numerical result out of range') extra={'repo': 'owner/repo', 'pr_number': 42, 'installation_id': 123456, 'error': "(34, 'Numerical result out of range')"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestDismissApproval::test_dismiss_approval_success - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 53 passed, 3 deselected in 0.79s
operator: core/ReplaceBinaryOperator_Sub_Pow, occurrence: 5
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -338,7 +338,7 @@
                 return True
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() ** start_time
                 github_api_request_duration_seconds.labels(operation="dismiss").observe(duration)
                 github_api_requests_total.labels(operation="dismiss", status="failure").inc()
 
......................................................F
=================================== FAILURES ===================================
______________ TestDismissApproval.test_dismiss_approval_failure _______________

self = <stampbot.github_client.GitHubAppClient object at 0x7f2b31926850>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
review_id = 999, message = 'Approval dismissed by Stampbot'

    def dismiss_approval(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        review_id: int,
        message: str = "Approval dismissed by Stampbot",
    ) -> bool:
        """Dismiss a pull request approval.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            review_id: Review ID to dismiss
            message: Dismissal message
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.dismiss_approval",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.review_id": review_id,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                # Get the review and dismiss it
                review = pr.get_review(review_id)
>               review.dismiss(message)

stampbot/github_client.py:318: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_pull().get_review().dismiss' id='139823490872192'>
args = ('Approval dismissed by Stampbot',), kwargs = {}
effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestDismissApproval object at 0x7f2b35884910>

    def test_dismiss_approval_failure(self):
        """Test approval dismissal failure."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_review = Mock()
            mock_review.dismiss.side_effect = Exception("API Error")
            mock_pr = Mock()
            mock_pr.get_review.return_value = mock_review
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.dismiss_approval(123456, "owner/repo", 42, 999)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:435: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f2b31926850>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
review_id = 999, message = 'Approval dismissed by Stampbot'

    def dismiss_approval(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        review_id: int,
        message: str = "Approval dismissed by Stampbot",
    ) -> bool:
        """Dismiss a pull request approval.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            review_id: Review ID to dismiss
            message: Dismissal message
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.dismiss_approval",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.review_id": review_id,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                # Get the review and dismiss it
                review = pr.get_review(review_id)
                review.dismiss(message)
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="dismiss").observe(duration)
                github_api_requests_total.labels(operation="dismiss", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.result": "dismissed"})
                set_span_ok(span)
    
                logger.info(
                    f"Dismissed approval for PR #{pr_number} in {repo_full_name}",
                    extra={
                        "repo": repo_full_name,
                        "pr_number": pr_number,
                        "installation_id": installation_id,
                        "review_id": review_id,
                    },
                )
                return True
    
            except Exception as e:
>               duration = time.time() ** start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^^
E               OverflowError: (34, 'Numerical result out of range')

stampbot/github_client.py:341: OverflowError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestDismissApproval::test_dismiss_approval_failure - OverflowError: (34, 'Numerical result out of range')
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 54 passed, 3 deselected in 0.86s
operator: core/ReplaceBinaryOperator_Sub_Pow, occurrence: 6
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -393,7 +393,7 @@
 
                 content = repo.get_contents(file_path, ref=ref)  # type: ignore[arg-type]
                 if isinstance(content, list):
-                    duration = time.time() - start_time
+                    duration = time.time() ** start_time
                     github_api_request_duration_seconds.labels(operation="get_file").observe(
                         duration
                     )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Pow, occurrence: 7
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -402,7 +402,7 @@
                     set_span_ok(span)
                     return None
 
-                duration = time.time() - start_time
+                duration = time.time() ** start_time
                 github_api_request_duration_seconds.labels(operation="get_file").observe(duration)
                 github_api_requests_total.labels(operation="get_file", status="success").inc()
 
.......................................................F
=================================== FAILURES ===================================
__________________ TestGetRepoFile.test_get_repo_file_success __________________

self = <tests.test_github_client.TestGetRepoFile object at 0x7ff2b8560a50>

    def test_get_repo_file_success(self):
        """Test successful file retrieval."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_content = Mock()
            mock_content.decoded_content = b"file content"
            mock_repo = Mock()
            mock_repo.get_contents.return_value = mock_content
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.get_repo_file(123456, "owner/repo", "stampbot.toml")
    
>           assert result == "file content"
E           AssertionError: assert None == 'file content'

tests/test_github_client.py:480: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:40:57 [debug    ] Could not fetch stampbot.toml from owner/repo: (34, 'Numerical result out of range') extra={'repo': 'owner/repo', 'file_path': 'stampbot.toml', 'installation_id': 123456}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetRepoFile::test_get_repo_file_success - AssertionError: assert None == 'file content'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 55 passed, 3 deselected in 0.73s
operator: core/ReplaceBinaryOperator_Sub_Pow, occurrence: 8
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -414,7 +414,7 @@
                 return content.decoded_content.decode("utf-8")
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() ** start_time
                 github_api_request_duration_seconds.labels(operation="get_file").observe(duration)
                 github_api_requests_total.labels(operation="get_file", status="not_found").inc()
 
.........................................................F
=================================== FAILURES ===================================
___________ TestGetRepoFile.test_get_repo_file_returns_none_on_error ___________

self = <stampbot.github_client.GitHubAppClient object at 0x7fa21335ccd0>
installation_id = 123456, repo_full_name = 'owner/repo'
file_path = 'nonexistent.toml', ref = None

    def get_repo_file(
        self,
        installation_id: int,
        repo_full_name: str,
        file_path: str,
        ref: str | None = None,
    ) -> str | None:
        """Get file content from repository.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            file_path: Path to file in repository
            ref: Git reference (branch, tag, commit). Defaults to default branch
    
        Returns:
            File content as string, or None if not found
        """
        start_time = time.time()
    
        with create_span(
            "github.get_file",
            {
                "github.repo": repo_full_name,
                "github.file_path": file_path,
                "github.ref": ref or "default",
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
    
>               content = repo.get_contents(file_path, ref=ref)  # type: ignore[arg-type]
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:394: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_contents' id='140334084031888'>
args = ('nonexistent.toml',), kwargs = {'ref': None}
effect = Exception('File not found')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: File not found

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestGetRepoFile object at 0x7fa217721940>

    def test_get_repo_file_returns_none_on_error(self):
        """Test that get_repo_file returns None when file not found."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_contents.side_effect = Exception("File not found")
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.get_repo_file(123456, "owner/repo", "nonexistent.toml")
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:551: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7fa21335ccd0>
installation_id = 123456, repo_full_name = 'owner/repo'
file_path = 'nonexistent.toml', ref = None

    def get_repo_file(
        self,
        installation_id: int,
        repo_full_name: str,
        file_path: str,
        ref: str | None = None,
    ) -> str | None:
        """Get file content from repository.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            file_path: Path to file in repository
            ref: Git reference (branch, tag, commit). Defaults to default branch
    
        Returns:
            File content as string, or None if not found
        """
        start_time = time.time()
    
        with create_span(
            "github.get_file",
            {
                "github.repo": repo_full_name,
                "github.file_path": file_path,
                "github.ref": ref or "default",
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
    
                content = repo.get_contents(file_path, ref=ref)  # type: ignore[arg-type]
                if isinstance(content, list):
                    duration = time.time() - start_time
                    github_api_request_duration_seconds.labels(operation="get_file").observe(
                        duration
                    )
                    github_api_requests_total.labels(operation="get_file", status="not_found").inc()
                    add_span_attributes(span, {"github.result": "not_found"})
                    set_span_ok(span)
                    return None
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_file").observe(duration)
                github_api_requests_total.labels(operation="get_file", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.result": "found"})
                set_span_ok(span)
    
                return content.decoded_content.decode("utf-8")
    
            except Exception as e:
>               duration = time.time() ** start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^^
E               OverflowError: (34, 'Numerical result out of range')

stampbot/github_client.py:417: OverflowError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetRepoFile::test_get_repo_file_returns_none_on_error - OverflowError: (34, 'Numerical result out of range')
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 57 passed, 3 deselected in 0.86s
operator: core/ReplaceBinaryOperator_Sub_Pow, occurrence: 9
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -473,7 +473,7 @@
                     if review.user.login == bot_user and review.state == "APPROVED":
                         bot_review_ids.append(review.id)
 
-                duration = time.time() - start_time
+                duration = time.time() ** start_time
                 github_api_request_duration_seconds.labels(operation="find_reviews").observe(
                     duration
                 )
..........................................................F
=================================== FAILURES ===================================
_______________ TestFindBotReviews.test_find_bot_reviews_success _______________

self = <tests.test_github_client.TestFindBotReviews object at 0x7f29aee5ccd0>

    def test_find_bot_reviews_success(self):
        """Test successful bot review finding."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration.get_app.return_value = Mock(slug="stampbot")
            mock_integration_cls.return_value = mock_integration
    
            # Create mock reviews
            bot_review = Mock()
            bot_review.user.login = "stampbot[bot]"
            bot_review.state = "APPROVED"
            bot_review.id = 123
    
            other_review = Mock()
            other_review.user.login = "other-user"
            other_review.state = "APPROVED"
            other_review.id = 456
    
            mock_pr = Mock()
            mock_pr.get_reviews.return_value = [bot_review, other_review]
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.find_bot_reviews(123456, "owner/repo", 42)
    
>           assert result == [123]
E           assert [] == [123]
E             
E             Right contains one more item: 123
E             
E             Full diff:
E             + []
E             - [
E             -     123,
E             - ]

tests/test_github_client.py:608: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 14:01:07 [error    ] Failed to find bot reviews for PR #42 in owner/repo: (34, 'Numerical result out of range') extra={'repo': 'owner/repo', 'pr_number': 42, 'installation_id': 123456, 'error': "(34, 'Numerical result out of range')"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestFindBotReviews::test_find_bot_reviews_success - assert [] == [123]
  
  Right contains one more item: 123
  
  Full diff:
  + []
  - [
  -     123,
  - ]
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 58 passed, 3 deselected in 0.74s
operator: core/ReplaceBinaryOperator_Sub_Pow, occurrence: 10
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -489,7 +489,7 @@
                 return bot_review_ids
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() ** start_time
                 github_api_request_duration_seconds.labels(operation="find_reviews").observe(
                     duration
                 )
...........................................................F
=================================== FAILURES ===================================
_______ TestFindBotReviews.test_find_bot_reviews_returns_empty_on_error ________

self = <stampbot.github_client.GitHubAppClient object at 0x7fb8f0d40f30>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42

    def find_bot_reviews(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
    ) -> list[int]:
        """Find all reviews created by this bot on a PR.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
    
        Returns:
            List of review IDs created by the bot
        """
        start_time = time.time()
    
        with create_span(
            "github.find_bot_reviews",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
>               repo = client.get_repo(repo_full_name)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:462: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo' id='140432291611040'>
args = ('owner/repo',), kwargs = {}, effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestFindBotReviews object at 0x7fb8f5164e10>

    def test_find_bot_reviews_returns_empty_on_error(self):
        """Test that find_bot_reviews returns empty list on error."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_github = Mock()
            mock_github.get_repo.side_effect = Exception("API Error")
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.find_bot_reviews(123456, "owner/repo", 42)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:640: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7fb8f0d40f30>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42

    def find_bot_reviews(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
    ) -> list[int]:
        """Find all reviews created by this bot on a PR.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
    
        Returns:
            List of review IDs created by the bot
        """
        start_time = time.time()
    
        with create_span(
            "github.find_bot_reviews",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                # Get bot user via JWT-authenticated integration (installation tokens
                # cannot call GET /user, which is restricted by GitHub Apps integration)
                app_info = self.integration.get_app()
                bot_user = f"{app_info.slug}[bot]"
    
                # Find all reviews by bot that are approvals
                bot_review_ids = []
                for review in pr.get_reviews():
                    if review.user.login == bot_user and review.state == "APPROVED":
                        bot_review_ids.append(review.id)
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="find_reviews").observe(
                    duration
                )
                github_api_requests_total.labels(operation="find_reviews", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(
                    span, {"github.reviews_found": len(bot_review_ids), "github.bot_user": bot_user}
                )
                set_span_ok(span)
    
                return bot_review_ids
    
            except Exception as e:
>               duration = time.time() ** start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^^
E               OverflowError: (34, 'Numerical result out of range')

stampbot/github_client.py:492: OverflowError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestFindBotReviews::test_find_bot_reviews_returns_empty_on_error - OverflowError: (34, 'Numerical result out of range')
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 59 passed, 3 deselected in 0.87s
operator: core/ReplaceBinaryOperator_Sub_Pow, occurrence: 11
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -546,7 +546,7 @@
 
                 pr.create_review(body=message, event="COMMENT")
 
-                duration = time.time() - start_time
+                duration = time.time() ** start_time
                 github_api_request_duration_seconds.labels(operation="comment").observe(duration)
                 github_api_requests_total.labels(operation="comment", status="success").inc()
 
........................................................................ [ 34%]
.F
=================================== FAILURES ===================================
_______ TestCreatePrReviewComment.test_create_pr_review_comment_success ________

self = <tests.test_github_client.TestCreatePrReviewComment object at 0x7f13397c96d0>

    def test_create_pr_review_comment_success(self):
        """Test successful review comment creation."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_pr = Mock()
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=1, limit=2))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.create_pr_review_comment(
                123456,
                "owner/repo",
                42,
                "Config error",
            )
    
>           assert result is True
E           assert False is True

tests/test_github_client.py:1213: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 14:10:31 [warning  ] Failed to post config error review for PR #42 in owner/repo: (34, 'Numerical result out of range') extra={'repo': 'owner/repo', 'pr_number': 42, 'installation_id': 123456, 'error': "(34, 'Numerical result out of range')"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestCreatePrReviewComment::test_create_pr_review_comment_success - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 73 passed, 3 deselected in 0.78s
operator: core/ReplaceBinaryOperator_Sub_Pow, occurrence: 12
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -568,7 +568,7 @@
                 return True
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() ** start_time
                 github_api_request_duration_seconds.labels(operation="comment").observe(duration)
                 github_api_requests_total.labels(operation="comment", status="failure").inc()
 
........................................................................ [ 34%]
..F
=================================== FAILURES ===================================
_______ TestCreatePrReviewComment.test_create_pr_review_comment_failure ________

self = <stampbot.github_client.GitHubAppClient object at 0x7fafc98b6db0>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
message = 'Config error'

    def create_pr_review_comment(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        message: str,
    ) -> bool:
        """Create a comment review on a pull request.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            message: Review comment body
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.create_review_comment",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
>               pr = repo.get_pull(pr_number)
                     ^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:545: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_pull' id='140392977287888'>
args = (42,), kwargs = {}, effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestCreatePrReviewComment object at 0x7fafcdb95810>

    def test_create_pr_review_comment_failure(self):
        """Test review comment creation handles errors."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_pull.side_effect = Exception("API Error")
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.create_pr_review_comment(
                123456,
                "owner/repo",
                42,
                "Config error",
            )

tests/test_github_client.py:1248: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7fafc98b6db0>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
message = 'Config error'

    def create_pr_review_comment(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        message: str,
    ) -> bool:
        """Create a comment review on a pull request.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            message: Review comment body
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.create_review_comment",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                pr.create_review(body=message, event="COMMENT")
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="comment").observe(duration)
                github_api_requests_total.labels(operation="comment", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.result": "commented"})
                set_span_ok(span)
    
                logger.info(
                    "Posted config error review on PR #%d in %s",
                    pr_number,
                    repo_full_name,
                    extra={
                        "repo": repo_full_name,
                        "pr_number": pr_number,
                        "installation_id": installation_id,
                    },
                )
                return True
    
            except Exception as e:
>               duration = time.time() ** start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^^
E               OverflowError: (34, 'Numerical result out of range')

stampbot/github_client.py:571: OverflowError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestCreatePrReviewComment::test_create_pr_review_comment_failure - OverflowError: (34, 'Numerical result out of range')
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 74 passed, 3 deselected in 0.91s
operator: core/ReplaceBinaryOperator_Sub_Pow, occurrence: 13
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -619,7 +619,7 @@
                 repo = client.get_repo(repo_full_name)
                 repo.get_label(label_name)
 
-                duration = time.time() - start_time
+                duration = time.time() ** start_time
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="success").inc()
 
.....................................................................F
=================================== FAILURES ===================================
__________________ TestRepoHasLabel.test_repo_has_label_true ___________________

self = <tests.test_github_client.TestRepoHasLabel object at 0x7f5a88a35450>

    def test_repo_has_label_true(self):
        """Test repo_has_label returns True when label exists."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_label.return_value = Mock()
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=1, limit=2))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.repo_has_label(123456, "owner/repo", "autoapprove")
    
>           assert result is True
E           assert None is True

tests/test_github_client.py:1054: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:27:37 [debug    ] Could not verify label autoapprove in owner/repo: (34, 'Numerical result out of range') extra={'repo': 'owner/repo', 'label': 'autoapprove', 'installation_id': 123456, 'error': "(34, 'Numerical result out of range')"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestRepoHasLabel::test_repo_has_label_true - assert None is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 69 passed, 3 deselected in 0.78s
operator: core/ReplaceBinaryOperator_Sub_Pow, occurrence: 14
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -630,7 +630,7 @@
                 return True
 
             except GithubException as e:
-                duration = time.time() - start_time
+                duration = time.time() ** start_time
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="failure").inc()
 
......................................................................F
=================================== FAILURES ===================================
________________ TestRepoHasLabel.test_repo_has_label_not_found ________________

self = <stampbot.github_client.GitHubAppClient object at 0x7f9de871d790>
installation_id = 123456, repo_full_name = 'owner/repo', label_name = 'missing'

    def repo_has_label(
        self,
        installation_id: int,
        repo_full_name: str,
        label_name: str,
    ) -> bool | None:
        """Check whether a repository has a label.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            label_name: Label name to look up
    
        Returns:
            True if label exists, False if not found, None if unknown due to error
        """
        start_time = time.time()
    
        with create_span(
            "github.get_label",
            {
                "github.repo": repo_full_name,
                "github.label": label_name,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
>               repo.get_label(label_name)

stampbot/github_client.py:620: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_label' id='140316188288944'>
args = ('missing',), kwargs = {}
effect = GithubException(404 {"message": "Not Found"})

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               github.GithubException.GithubException: 404 {"message": "Not Found"}

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: GithubException

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestRepoHasLabel object at 0x7f9decce9590>

    def test_repo_has_label_not_found(self):
        """Test repo_has_label returns False when label is missing."""
        from github.GithubException import GithubException
    
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_label.side_effect = GithubException(404, {"message": "Not Found"}, None)
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.repo_has_label(123456, "owner/repo", "missing")
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:1090: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f9de871d790>
installation_id = 123456, repo_full_name = 'owner/repo', label_name = 'missing'

    def repo_has_label(
        self,
        installation_id: int,
        repo_full_name: str,
        label_name: str,
    ) -> bool | None:
        """Check whether a repository has a label.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            label_name: Label name to look up
    
        Returns:
            True if label exists, False if not found, None if unknown due to error
        """
        start_time = time.time()
    
        with create_span(
            "github.get_label",
            {
                "github.repo": repo_full_name,
                "github.label": label_name,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                repo.get_label(label_name)
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                github_api_requests_total.labels(operation="get_label", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.label_found": True})
                set_span_ok(span)
                return True
    
            except GithubException as e:
>               duration = time.time() ** start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^^
E               OverflowError: (34, 'Numerical result out of range')

stampbot/github_client.py:633: OverflowError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestRepoHasLabel::test_repo_has_label_not_found - OverflowError: (34, 'Numerical result out of range')
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 70 passed, 3 deselected in 0.91s
operator: core/ReplaceBinaryOperator_Sub_Pow, occurrence: 15
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -655,7 +655,7 @@
                 return None
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() ** start_time
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="failure").inc()
 
........................................................................ [ 34%]
F
=================================== FAILURES ===================================
__________________ TestRepoHasLabel.test_repo_has_label_error __________________

self = <stampbot.github_client.GitHubAppClient object at 0x7fb27a49f4d0>
installation_id = 123456, repo_full_name = 'owner/repo'
label_name = 'autoapprove'

    def repo_has_label(
        self,
        installation_id: int,
        repo_full_name: str,
        label_name: str,
    ) -> bool | None:
        """Check whether a repository has a label.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            label_name: Label name to look up
    
        Returns:
            True if label exists, False if not found, None if unknown due to error
        """
        start_time = time.time()
    
        with create_span(
            "github.get_label",
            {
                "github.repo": repo_full_name,
                "github.label": label_name,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
>               repo.get_label(label_name)

stampbot/github_client.py:620: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_label' id='140404532467024'>
args = ('autoapprove',), kwargs = {}, effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestRepoHasLabel object at 0x7fb27e87e060>

    def test_repo_has_label_error(self):
        """Test repo_has_label returns None on error."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_label.side_effect = Exception("API Error")
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.repo_has_label(123456, "owner/repo", "autoapprove")
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:1164: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7fb27a49f4d0>
installation_id = 123456, repo_full_name = 'owner/repo'
label_name = 'autoapprove'

    def repo_has_label(
        self,
        installation_id: int,
        repo_full_name: str,
        label_name: str,
    ) -> bool | None:
        """Check whether a repository has a label.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            label_name: Label name to look up
    
        Returns:
            True if label exists, False if not found, None if unknown due to error
        """
        start_time = time.time()
    
        with create_span(
            "github.get_label",
            {
                "github.repo": repo_full_name,
                "github.label": label_name,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                repo.get_label(label_name)
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                github_api_requests_total.labels(operation="get_label", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.label_found": True})
                set_span_ok(span)
                return True
    
            except GithubException as e:
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                github_api_requests_total.labels(operation="get_label", status="failure").inc()
    
                if e.status == 404:
                    add_span_attributes(span, {"github.label_found": False})
                    set_span_ok(span)
                    return False
    
                set_span_error(span, e)
                logger.debug(
                    "Could not verify label %s in %s: %s",
                    label_name,
                    repo_full_name,
                    _sanitize_error(e),
                    extra={
                        "repo": repo_full_name,
                        "label": label_name,
                        "installation_id": installation_id,
                        "error": _sanitize_error(e),
                    },
                )
                return None
    
            except Exception as e:
>               duration = time.time() ** start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^^
E               OverflowError: (34, 'Numerical result out of range')

stampbot/github_client.py:658: OverflowError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestRepoHasLabel::test_repo_has_label_error - OverflowError: (34, 'Numerical result out of range')
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 72 passed, 3 deselected in 0.91s
operator: core/ReplaceBinaryOperator_Sub_Pow, occurrence: 16
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -718,7 +718,7 @@
 
                 has_permission = permission_index >= required_index
 
-                duration = time.time() - start_time
+                duration = time.time() ** start_time
                 github_api_request_duration_seconds.labels(operation="get_permission").observe(
                     duration
                 )
............................................................F
=================================== FAILURES ===================================
_____________ TestUserHasPermission.test_user_has_permission_true ______________

self = <tests.test_github_client.TestUserHasPermission object at 0x7f9210798f50>

    def test_user_has_permission_true(self):
        """Test required permission satisfied returns True."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_collaborator_permission.return_value = "write"
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=1, limit=2))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.user_has_permission(123456, "owner/repo", "alice", "write")
    
>           assert result is True
E           assert False is True

tests/test_github_client.py:683: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:48:43 [warning  ] Failed to check collaborator permission for alice in owner/repo: (34, 'Numerical result out of range') extra={'repo': 'owner/repo', 'username': 'alice', 'installation_id': 123456, 'error': "(34, 'Numerical result out of range')"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestUserHasPermission::test_user_has_permission_true - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 60 passed, 3 deselected in 0.75s
operator: core/ReplaceBinaryOperator_Sub_Pow, occurrence: 17
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -738,7 +738,7 @@
                 return has_permission
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() ** start_time
                 github_api_request_duration_seconds.labels(operation="get_permission").observe(
                     duration
                 )
..............................................................F
=================================== FAILURES ===================================
_____________ TestUserHasPermission.test_user_has_permission_error _____________

self = <stampbot.github_client.GitHubAppClient object at 0x7fbe53e6fa10>
installation_id = 123456, repo_full_name = 'owner/repo', username = 'carol'
required_permission = 'maintain'

    def user_has_permission(
        self,
        installation_id: int,
        repo_full_name: str,
        username: str,
        required_permission: str,
    ) -> bool:
        """Check whether a user has required access to a repository.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            username: GitHub username to check
            required_permission: Minimum required repository permission
    
        Returns:
            True if user meets or exceeds required permission, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.get_collaborator_permission",
            {
                "github.repo": repo_full_name,
                "github.username": username,
                "github.required_permission": required_permission,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
    
>               permission = repo.get_collaborator_permission(username)
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:710: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_collaborator_permission' id='140455428115152'>
args = ('carol',), kwargs = {}, effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestUserHasPermission object at 0x7fbe58109a70>

    def test_user_has_permission_error(self):
        """Test permission check returns False on error."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_collaborator_permission.side_effect = Exception("API Error")
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.user_has_permission(123456, "owner/repo", "carol", "maintain")
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:754: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7fbe53e6fa10>
installation_id = 123456, repo_full_name = 'owner/repo', username = 'carol'
required_permission = 'maintain'

    def user_has_permission(
        self,
        installation_id: int,
        repo_full_name: str,
        username: str,
        required_permission: str,
    ) -> bool:
        """Check whether a user has required access to a repository.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            username: GitHub username to check
            required_permission: Minimum required repository permission
    
        Returns:
            True if user meets or exceeds required permission, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.get_collaborator_permission",
            {
                "github.repo": repo_full_name,
                "github.username": username,
                "github.required_permission": required_permission,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
    
                permission = repo.get_collaborator_permission(username)
                permission_order = ["none", "read", "triage", "write", "maintain", "admin"]
                try:
                    permission_index = permission_order.index(permission)
                    required_index = permission_order.index(required_permission)
                except ValueError:
                    permission_index = -1
                    required_index = 99
    
                has_permission = permission_index >= required_index
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_permission").observe(
                    duration
                )
                github_api_requests_total.labels(operation="get_permission", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(
                    span,
                    {
                        "github.permission": permission,
                        "github.has_permission": has_permission,
                    },
                )
                set_span_ok(span)
    
                return has_permission
    
            except Exception as e:
>               duration = time.time() ** start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^^
E               OverflowError: (34, 'Numerical result out of range')

stampbot/github_client.py:741: OverflowError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestUserHasPermission::test_user_has_permission_error - OverflowError: (34, 'Numerical result out of range')
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 62 passed, 3 deselected in 0.88s
operator: core/ReplaceBinaryOperator_Sub_Pow, occurrence: 18
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -829,7 +829,7 @@
                         )
                         continue
 
-                duration = time.time() - start_time
+                duration = time.time() ** start_time
                 github_api_request_duration_seconds.labels(operation="get_user_teams").observe(
                     duration
                 )
................................................................F
=================================== FAILURES ===================================
____________ TestGetUserTeamSlugs.test_get_user_team_slugs_success _____________

self = <tests.test_github_client.TestGetUserTeamSlugs object at 0x7fb429dfd1d0>

    def test_get_user_team_slugs_success(self):
        """Test successful team membership check."""
    
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            # Mock user
            mock_user = Mock()
            mock_user.login = "alice"
    
            # Mock team that has alice as member
            mock_team = Mock()
            mock_team.has_in_members.return_value = True
    
            # Mock org
            mock_org = Mock()
            mock_org.get_team_by_slug.return_value = mock_team
    
            mock_github = Mock()
            mock_github.get_organization.return_value = mock_org
            mock_github.get_user.return_value = mock_user
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.get_user_team_slugs(
                123456, "acme", "alice", ["release-team", "deploy-team"]
            )
    
>           assert result == ["release-team", "deploy-team"]
E           AssertionError: assert [] == ['release-tea...'deploy-team']
E             
E             Right contains 2 more items, first extra item: 'release-team'
E             
E             Full diff:
E             + []
E             - [
E             -     'release-team',
E             -     'deploy-team',
E             - ]

tests/test_github_client.py:848: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:33:29 [debug    ] User alice is a member of team release-team extra={'org': 'acme', 'username': 'alice', 'team': 'release-team'}
2026-03-27 13:33:29 [debug    ] User alice is a member of team deploy-team extra={'org': 'acme', 'username': 'alice', 'team': 'deploy-team'}
2026-03-27 13:33:29 [warning  ] Failed to check team memberships for alice in acme: (34, 'Numerical result out of range') extra={'org': 'acme', 'username': 'alice', 'installation_id': 123456, 'error': "(34, 'Numerical result out of range')"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetUserTeamSlugs::test_get_user_team_slugs_success - AssertionError: assert [] == ['release-tea...'deploy-team']
  
  Right contains 2 more items, first extra item: 'release-team'
  
  Full diff:
  + []
  - [
  -     'release-team',
  -     'deploy-team',
  - ]
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 64 passed, 3 deselected in 0.76s
operator: core/ReplaceBinaryOperator_Sub_Pow, occurrence: 19
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -849,7 +849,7 @@
                 return member_teams
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() ** start_time
                 github_api_request_duration_seconds.labels(operation="get_user_teams").observe(
                     duration
                 )
....................................................................F
=================================== FAILURES ===================================
_______ TestGetUserTeamSlugs.test_get_user_team_slugs_general_exception ________

self = <stampbot.github_client.GitHubAppClient object at 0x7f71d6f16270>
installation_id = 123456, org_name = 'acme', username = 'alice'
allowed_teams = ['release-team']

    def get_user_team_slugs(
        self,
        installation_id: int,
        org_name: str,
        username: str,
        allowed_teams: list[str],
    ) -> list[str]:
        """Get team slugs the user is a member of from the allowed teams list.
    
        Args:
            installation_id: GitHub App installation ID
            org_name: Organization name
            username: GitHub username to check
            allowed_teams: List of team slugs to check (can be "org/team" or just "team")
    
        Returns:
            List of team slugs the user is a member of
        """
        start_time = time.time()
    
        with create_span(
            "github.get_user_team_slugs",
            {
                "github.org": org_name,
                "github.username": username,
                "github.installation_id": installation_id,
                "github.teams_to_check": len(allowed_teams),
            },
        ) as span:
            member_teams: list[str] = []
    
            try:
                client = self._get_installation_client(installation_id)
>               org = client.get_organization(org_name)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:796: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_organization' id='140126915999600'>
args = ('acme',), kwargs = {}, effect = Exception('Network error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: Network error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestGetUserTeamSlugs object at 0x7f71db5485f0>

    def test_get_user_team_slugs_general_exception(self):
        """Test team membership check with general exception."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_github = Mock()
            mock_github.get_organization.side_effect = Exception("Network error")
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.get_user_team_slugs(123456, "acme", "alice", ["release-team"])
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:1010: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f71d6f16270>
installation_id = 123456, org_name = 'acme', username = 'alice'
allowed_teams = ['release-team']

    def get_user_team_slugs(
        self,
        installation_id: int,
        org_name: str,
        username: str,
        allowed_teams: list[str],
    ) -> list[str]:
        """Get team slugs the user is a member of from the allowed teams list.
    
        Args:
            installation_id: GitHub App installation ID
            org_name: Organization name
            username: GitHub username to check
            allowed_teams: List of team slugs to check (can be "org/team" or just "team")
    
        Returns:
            List of team slugs the user is a member of
        """
        start_time = time.time()
    
        with create_span(
            "github.get_user_team_slugs",
            {
                "github.org": org_name,
                "github.username": username,
                "github.installation_id": installation_id,
                "github.teams_to_check": len(allowed_teams),
            },
        ) as span:
            member_teams: list[str] = []
    
            try:
                client = self._get_installation_client(installation_id)
                org = client.get_organization(org_name)
    
                for team_ref in allowed_teams:
                    # Extract team slug (handle both "org/team" and "team" formats)
                    team_slug = team_ref.split("/")[-1] if "/" in team_ref else team_ref
    
                    try:
                        team = org.get_team_by_slug(team_slug)
                        # Check if user is a member
                        user = client.get_user(username)
                        if team.has_in_members(user):  # type: ignore[arg-type]
                            member_teams.append(team_slug)
                            logger.debug(
                                "User %s is a member of team %s",
                                username,
                                team_slug,
                                extra={
                                    "org": org_name,
                                    "username": username,
                                    "team": team_slug,
                                },
                            )
                    except GithubException as e:
                        # Team not found or no access - skip silently
                        logger.debug(
                            "Could not check team %s membership: %s",
                            team_slug,
                            e.data.get("message", str(e)) if hasattr(e, "data") else str(e),
                            extra={
                                "org": org_name,
                                "team": team_slug,
                                "username": username,
                            },
                        )
                        continue
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_user_teams").observe(
                    duration
                )
                github_api_requests_total.labels(operation="get_user_teams", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(
                    span,
                    {
                        "github.member_teams": len(member_teams),
                        "github.teams_checked": len(allowed_teams),
                    },
                )
                set_span_ok(span)
    
                return member_teams
    
            except Exception as e:
>               duration = time.time() ** start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^^
E               OverflowError: (34, 'Numerical result out of range')

stampbot/github_client.py:852: OverflowError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetUserTeamSlugs::test_get_user_team_slugs_general_exception - OverflowError: (34, 'Numerical result out of range')
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 68 passed, 3 deselected in 0.88s
operator: core/ReplaceBinaryOperator_Sub_RShift, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -168,7 +168,7 @@
                     retry=retry,
                 )
 
-                duration = time.time() - start_time
+                duration = time.time() >> start_time
                 github_api_request_duration_seconds.labels(operation="get_token").observe(duration)
                 github_api_requests_total.labels(operation="get_token", status="success").inc()
 
...............................................F
=================================== FAILURES ===================================
________ TestGetInstallationClient.test_get_installation_client_success ________

self = <tests.test_github_client.TestGetInstallationClient object at 0x7f94990e4050>

    def test_get_installation_client_success(self):
        """Test successful installation client creation."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client._get_installation_client(123456)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:211: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f949529c830>
installation_id = 123456

    def _get_installation_client(self, installation_id: int) -> Github:
        """Get authenticated GitHub client for an installation.
    
        Args:
            installation_id: GitHub App installation ID.
    
        Returns:
            Authenticated Github client instance with timeout and retry configured.
    
        Raises:
            github.GithubException: If token exchange fails.
        """
        start_time = time.time()
    
        with create_span(
            "github.get_installation_token",
            {"github.installation_id": installation_id},
        ) as span:
            try:
                auth = self.integration.get_access_token(installation_id)
    
                # Configure retry with exponential backoff
                retry = Retry(
                    total=GITHUB_API_RETRY_TOTAL,
                    backoff_factor=GITHUB_API_RETRY_BACKOFF,
                    status_forcelist=[500, 502, 503, 504],
                    allowed_methods=["GET", "POST", "PUT", "DELETE", "PATCH"],
                )
    
                client = Github(
                    auth=Auth.Token(auth.token),
                    timeout=GITHUB_API_TIMEOUT,
                    retry=retry,
                )
    
>               duration = time.time() >> start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for >>: 'float' and 'float'

stampbot/github_client.py:171: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetInstallationClient::test_get_installation_client_success - TypeError: unsupported operand type(s) for >>: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 47 passed, 3 deselected in 0.77s
operator: core/ReplaceBinaryOperator_Sub_RShift, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -176,7 +176,7 @@
                 return client
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() >> start_time
                 github_api_request_duration_seconds.labels(operation="get_token").observe(duration)
                 github_api_requests_total.labels(operation="get_token", status="failure").inc()
                 set_span_error(span, e)
................................................F
=================================== FAILURES ===================================
________ TestGetInstallationClient.test_get_installation_client_failure ________

self = <stampbot.github_client.GitHubAppClient object at 0x7fcac46a9230>
installation_id = 123456

    def _get_installation_client(self, installation_id: int) -> Github:
        """Get authenticated GitHub client for an installation.
    
        Args:
            installation_id: GitHub App installation ID.
    
        Returns:
            Authenticated Github client instance with timeout and retry configured.
    
        Raises:
            github.GithubException: If token exchange fails.
        """
        start_time = time.time()
    
        with create_span(
            "github.get_installation_token",
            {"github.installation_id": installation_id},
        ) as span:
            try:
>               auth = self.integration.get_access_token(installation_id)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:155: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='GithubIntegration().get_access_token' id='140508836036096'>
args = (123456,), kwargs = {}, effect = Exception('Token exchange failed')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: Token exchange failed

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestGetInstallationClient object at 0x7fcac7474190>

    def test_get_installation_client_failure(self):
        """Test installation client creation failure."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_integration.get_access_token.side_effect = Exception("Token exchange failed")
            mock_integration_cls.return_value = mock_integration
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            with pytest.raises(Exception, match="Token exchange failed"):
>               client._get_installation_client(123456)

tests/test_github_client.py:240: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7fcac46a9230>
installation_id = 123456

    def _get_installation_client(self, installation_id: int) -> Github:
        """Get authenticated GitHub client for an installation.
    
        Args:
            installation_id: GitHub App installation ID.
    
        Returns:
            Authenticated Github client instance with timeout and retry configured.
    
        Raises:
            github.GithubException: If token exchange fails.
        """
        start_time = time.time()
    
        with create_span(
            "github.get_installation_token",
            {"github.installation_id": installation_id},
        ) as span:
            try:
                auth = self.integration.get_access_token(installation_id)
    
                # Configure retry with exponential backoff
                retry = Retry(
                    total=GITHUB_API_RETRY_TOTAL,
                    backoff_factor=GITHUB_API_RETRY_BACKOFF,
                    status_forcelist=[500, 502, 503, 504],
                    allowed_methods=["GET", "POST", "PUT", "DELETE", "PATCH"],
                )
    
                client = Github(
                    auth=Auth.Token(auth.token),
                    timeout=GITHUB_API_TIMEOUT,
                    retry=retry,
                )
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_token").observe(duration)
                github_api_requests_total.labels(operation="get_token", status="success").inc()
    
                set_span_ok(span)
                return client
    
            except Exception as e:
>               duration = time.time() >> start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for >>: 'float' and 'float'

stampbot/github_client.py:179: TypeError

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestGetInstallationClient object at 0x7fcac7474190>

    def test_get_installation_client_failure(self):
        """Test installation client creation failure."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_integration.get_access_token.side_effect = Exception("Token exchange failed")
            mock_integration_cls.return_value = mock_integration
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           with pytest.raises(Exception, match="Token exchange failed"):
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E           AssertionError: Regex pattern did not match.
E             Expected regex: 'Token exchange failed'
E             Actual message: "unsupported operand type(s) for >>: 'float' and 'float'"

tests/test_github_client.py:239: AssertionError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetInstallationClient::test_get_installation_client_failure - AssertionError: Regex pattern did not match.
  Expected regex: 'Token exchange failed'
  Actual message: "unsupported operand type(s) for >>: 'float' and 'float'"
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 48 passed, 3 deselected in 0.87s
operator: core/ReplaceBinaryOperator_Sub_RShift, occurrence: 2
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -240,7 +240,7 @@
                     event="APPROVE",
                 )
 
-                duration = time.time() - start_time
+                duration = time.time() >> start_time
                 github_api_request_duration_seconds.labels(operation="approve").observe(duration)
                 github_api_requests_total.labels(operation="approve", status="success").inc()
 
...................................................F
=================================== FAILURES ===================================
____________________ TestApprovePR.test_approve_pr_success _____________________

self = <tests.test_github_client.TestApprovePR object at 0x7f024df84550>

    def test_approve_pr_success(self):
        """Test successful PR approval."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_pr = Mock()
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.approve_pr(123456, "owner/repo", 42)
    
>           assert result is True
E           assert False is True

tests/test_github_client.py:313: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:39:10 [error    ] Failed to approve PR #42 in owner/repo: unsupported operand type(s) for >>: 'float' and 'float' extra={'repo': 'owner/repo', 'pr_number': 42, 'installation_id': 123456, 'error': "unsupported operand type(s) for >>: 'float' and 'float'"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestApprovePR::test_approve_pr_success - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 51 passed, 3 deselected in 0.74s
operator: core/ReplaceBinaryOperator_Sub_RShift, occurrence: 3
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -260,7 +260,7 @@
                 return True
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() >> start_time
                 github_api_request_duration_seconds.labels(operation="approve").observe(duration)
                 github_api_requests_total.labels(operation="approve", status="failure").inc()
 
....................................................F
=================================== FAILURES ===================================
____________________ TestApprovePR.test_approve_pr_failure _____________________

self = <stampbot.github_client.GitHubAppClient object at 0x7ff721e6ec50>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
comment = 'Auto-approved by Stampbot'

    def approve_pr(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        comment: str = "Auto-approved by Stampbot",
    ) -> bool:
        """Approve a pull request.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            comment: Review comment
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.approve_pr",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                # Create approval review
>               pr.create_review(
                    body=comment,
                    event="APPROVE",
                )

stampbot/github_client.py:238: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_pull().create_review' id='140699402827968'>
args = (), kwargs = {'body': 'Auto-approved by Stampbot', 'event': 'APPROVE'}
effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestApprovePR object at 0x7ff726288690>

    def test_approve_pr_failure(self):
        """Test PR approval failure."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_pr = Mock()
            mock_pr.create_review.side_effect = Exception("API Error")
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.approve_pr(123456, "owner/repo", 42)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:350: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7ff721e6ec50>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
comment = 'Auto-approved by Stampbot'

    def approve_pr(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        comment: str = "Auto-approved by Stampbot",
    ) -> bool:
        """Approve a pull request.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            comment: Review comment
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.approve_pr",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                # Create approval review
                pr.create_review(
                    body=comment,
                    event="APPROVE",
                )
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="approve").observe(duration)
                github_api_requests_total.labels(operation="approve", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.result": "approved"})
                set_span_ok(span)
    
                logger.info(
                    f"Approved PR #{pr_number} in {repo_full_name}",
                    extra={
                        "repo": repo_full_name,
                        "pr_number": pr_number,
                        "installation_id": installation_id,
                    },
                )
                return True
    
            except Exception as e:
>               duration = time.time() >> start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for >>: 'float' and 'float'

stampbot/github_client.py:263: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestApprovePR::test_approve_pr_failure - TypeError: unsupported operand type(s) for >>: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 52 passed, 3 deselected in 0.86s
operator: core/ReplaceBinaryOperator_Sub_RShift, occurrence: 4
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -317,7 +317,7 @@
                 review = pr.get_review(review_id)
                 review.dismiss(message)
 
-                duration = time.time() - start_time
+                duration = time.time() >> start_time
                 github_api_request_duration_seconds.labels(operation="dismiss").observe(duration)
                 github_api_requests_total.labels(operation="dismiss", status="success").inc()
 
.....................................................F
=================================== FAILURES ===================================
______________ TestDismissApproval.test_dismiss_approval_success _______________

self = <tests.test_github_client.TestDismissApproval object at 0x7f5ba69a47d0>

    def test_dismiss_approval_success(self):
        """Test successful approval dismissal."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_review = Mock()
            mock_pr = Mock()
            mock_pr.get_review.return_value = mock_review
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.dismiss_approval(123456, "owner/repo", 42, 999)
    
>           assert result is True
E           assert False is True

tests/test_github_client.py:396: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:26:11 [error    ] Failed to dismiss approval for PR #42 in owner/repo: unsupported operand type(s) for >>: 'float' and 'float' extra={'repo': 'owner/repo', 'pr_number': 42, 'installation_id': 123456, 'error': "unsupported operand type(s) for >>: 'float' and 'float'"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestDismissApproval::test_dismiss_approval_success - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 53 passed, 3 deselected in 0.75s
operator: core/ReplaceBinaryOperator_Sub_RShift, occurrence: 5
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -338,7 +338,7 @@
                 return True
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() >> start_time
                 github_api_request_duration_seconds.labels(operation="dismiss").observe(duration)
                 github_api_requests_total.labels(operation="dismiss", status="failure").inc()
 
......................................................F
=================================== FAILURES ===================================
______________ TestDismissApproval.test_dismiss_approval_failure _______________

self = <stampbot.github_client.GitHubAppClient object at 0x7ff19efee850>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
review_id = 999, message = 'Approval dismissed by Stampbot'

    def dismiss_approval(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        review_id: int,
        message: str = "Approval dismissed by Stampbot",
    ) -> bool:
        """Dismiss a pull request approval.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            review_id: Review ID to dismiss
            message: Dismissal message
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.dismiss_approval",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.review_id": review_id,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                # Get the review and dismiss it
                review = pr.get_review(review_id)
>               review.dismiss(message)

stampbot/github_client.py:318: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_pull().get_review().dismiss' id='140675730224000'>
args = ('Approval dismissed by Stampbot',), kwargs = {}
effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestDismissApproval object at 0x7ff1a2e6c910>

    def test_dismiss_approval_failure(self):
        """Test approval dismissal failure."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_review = Mock()
            mock_review.dismiss.side_effect = Exception("API Error")
            mock_pr = Mock()
            mock_pr.get_review.return_value = mock_review
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.dismiss_approval(123456, "owner/repo", 42, 999)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:435: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7ff19efee850>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
review_id = 999, message = 'Approval dismissed by Stampbot'

    def dismiss_approval(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        review_id: int,
        message: str = "Approval dismissed by Stampbot",
    ) -> bool:
        """Dismiss a pull request approval.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            review_id: Review ID to dismiss
            message: Dismissal message
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.dismiss_approval",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.review_id": review_id,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                # Get the review and dismiss it
                review = pr.get_review(review_id)
                review.dismiss(message)
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="dismiss").observe(duration)
                github_api_requests_total.labels(operation="dismiss", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.result": "dismissed"})
                set_span_ok(span)
    
                logger.info(
                    f"Dismissed approval for PR #{pr_number} in {repo_full_name}",
                    extra={
                        "repo": repo_full_name,
                        "pr_number": pr_number,
                        "installation_id": installation_id,
                        "review_id": review_id,
                    },
                )
                return True
    
            except Exception as e:
>               duration = time.time() >> start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for >>: 'float' and 'float'

stampbot/github_client.py:341: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestDismissApproval::test_dismiss_approval_failure - TypeError: unsupported operand type(s) for >>: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 54 passed, 3 deselected in 0.86s
operator: core/ReplaceBinaryOperator_Sub_RShift, occurrence: 6
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -393,7 +393,7 @@
 
                 content = repo.get_contents(file_path, ref=ref)  # type: ignore[arg-type]
                 if isinstance(content, list):
-                    duration = time.time() - start_time
+                    duration = time.time() >> start_time
                     github_api_request_duration_seconds.labels(operation="get_file").observe(
                         duration
                     )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_RShift, occurrence: 7
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -402,7 +402,7 @@
                     set_span_ok(span)
                     return None
 
-                duration = time.time() - start_time
+                duration = time.time() >> start_time
                 github_api_request_duration_seconds.labels(operation="get_file").observe(duration)
                 github_api_requests_total.labels(operation="get_file", status="success").inc()
 
.......................................................F
=================================== FAILURES ===================================
__________________ TestGetRepoFile.test_get_repo_file_success __________________

self = <tests.test_github_client.TestGetRepoFile object at 0x7fe6210a0a50>

    def test_get_repo_file_success(self):
        """Test successful file retrieval."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_content = Mock()
            mock_content.decoded_content = b"file content"
            mock_repo = Mock()
            mock_repo.get_contents.return_value = mock_content
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.get_repo_file(123456, "owner/repo", "stampbot.toml")
    
>           assert result == "file content"
E           AssertionError: assert None == 'file content'

tests/test_github_client.py:480: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:49:02 [debug    ] Could not fetch stampbot.toml from owner/repo: unsupported operand type(s) for >>: 'float' and 'float' extra={'repo': 'owner/repo', 'file_path': 'stampbot.toml', 'installation_id': 123456}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetRepoFile::test_get_repo_file_success - AssertionError: assert None == 'file content'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 55 passed, 3 deselected in 0.75s
operator: core/ReplaceBinaryOperator_Sub_RShift, occurrence: 8
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -414,7 +414,7 @@
                 return content.decoded_content.decode("utf-8")
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() >> start_time
                 github_api_request_duration_seconds.labels(operation="get_file").observe(duration)
                 github_api_requests_total.labels(operation="get_file", status="not_found").inc()
 
.........................................................F
=================================== FAILURES ===================================
___________ TestGetRepoFile.test_get_repo_file_returns_none_on_error ___________

self = <stampbot.github_client.GitHubAppClient object at 0x7f7b00850cd0>
installation_id = 123456, repo_full_name = 'owner/repo'
file_path = 'nonexistent.toml', ref = None

    def get_repo_file(
        self,
        installation_id: int,
        repo_full_name: str,
        file_path: str,
        ref: str | None = None,
    ) -> str | None:
        """Get file content from repository.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            file_path: Path to file in repository
            ref: Git reference (branch, tag, commit). Defaults to default branch
    
        Returns:
            File content as string, or None if not found
        """
        start_time = time.time()
    
        with create_span(
            "github.get_file",
            {
                "github.repo": repo_full_name,
                "github.file_path": file_path,
                "github.ref": ref or "default",
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
    
>               content = repo.get_contents(file_path, ref=ref)  # type: ignore[arg-type]
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:394: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_contents' id='140166266733968'>
args = ('nonexistent.toml',), kwargs = {'ref': None}
effect = Exception('File not found')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: File not found

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestGetRepoFile object at 0x7f7b04bcd940>

    def test_get_repo_file_returns_none_on_error(self):
        """Test that get_repo_file returns None when file not found."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_contents.side_effect = Exception("File not found")
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.get_repo_file(123456, "owner/repo", "nonexistent.toml")
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:551: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f7b00850cd0>
installation_id = 123456, repo_full_name = 'owner/repo'
file_path = 'nonexistent.toml', ref = None

    def get_repo_file(
        self,
        installation_id: int,
        repo_full_name: str,
        file_path: str,
        ref: str | None = None,
    ) -> str | None:
        """Get file content from repository.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            file_path: Path to file in repository
            ref: Git reference (branch, tag, commit). Defaults to default branch
    
        Returns:
            File content as string, or None if not found
        """
        start_time = time.time()
    
        with create_span(
            "github.get_file",
            {
                "github.repo": repo_full_name,
                "github.file_path": file_path,
                "github.ref": ref or "default",
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
    
                content = repo.get_contents(file_path, ref=ref)  # type: ignore[arg-type]
                if isinstance(content, list):
                    duration = time.time() - start_time
                    github_api_request_duration_seconds.labels(operation="get_file").observe(
                        duration
                    )
                    github_api_requests_total.labels(operation="get_file", status="not_found").inc()
                    add_span_attributes(span, {"github.result": "not_found"})
                    set_span_ok(span)
                    return None
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_file").observe(duration)
                github_api_requests_total.labels(operation="get_file", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.result": "found"})
                set_span_ok(span)
    
                return content.decoded_content.decode("utf-8")
    
            except Exception as e:
>               duration = time.time() >> start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for >>: 'float' and 'float'

stampbot/github_client.py:417: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetRepoFile::test_get_repo_file_returns_none_on_error - TypeError: unsupported operand type(s) for >>: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 57 passed, 3 deselected in 0.86s
operator: core/ReplaceBinaryOperator_Sub_RShift, occurrence: 9
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -473,7 +473,7 @@
                     if review.user.login == bot_user and review.state == "APPROVED":
                         bot_review_ids.append(review.id)
 
-                duration = time.time() - start_time
+                duration = time.time() >> start_time
                 github_api_request_duration_seconds.labels(operation="find_reviews").observe(
                     duration
                 )
..........................................................F
=================================== FAILURES ===================================
_______________ TestFindBotReviews.test_find_bot_reviews_success _______________

self = <tests.test_github_client.TestFindBotReviews object at 0x7fc6ead18cd0>

    def test_find_bot_reviews_success(self):
        """Test successful bot review finding."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration.get_app.return_value = Mock(slug="stampbot")
            mock_integration_cls.return_value = mock_integration
    
            # Create mock reviews
            bot_review = Mock()
            bot_review.user.login = "stampbot[bot]"
            bot_review.state = "APPROVED"
            bot_review.id = 123
    
            other_review = Mock()
            other_review.user.login = "other-user"
            other_review.state = "APPROVED"
            other_review.id = 456
    
            mock_pr = Mock()
            mock_pr.get_reviews.return_value = [bot_review, other_review]
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.find_bot_reviews(123456, "owner/repo", 42)
    
>           assert result == [123]
E           assert [] == [123]
E             
E             Right contains one more item: 123
E             
E             Full diff:
E             + []
E             - [
E             -     123,
E             - ]

tests/test_github_client.py:608: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:59:15 [error    ] Failed to find bot reviews for PR #42 in owner/repo: unsupported operand type(s) for >>: 'float' and 'float' extra={'repo': 'owner/repo', 'pr_number': 42, 'installation_id': 123456, 'error': "unsupported operand type(s) for >>: 'float' and 'float'"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestFindBotReviews::test_find_bot_reviews_success - assert [] == [123]
  
  Right contains one more item: 123
  
  Full diff:
  + []
  - [
  -     123,
  - ]
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 58 passed, 3 deselected in 0.75s
operator: core/ReplaceBinaryOperator_Sub_RShift, occurrence: 10
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -489,7 +489,7 @@
                 return bot_review_ids
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() >> start_time
                 github_api_request_duration_seconds.labels(operation="find_reviews").observe(
                     duration
                 )
...........................................................F
=================================== FAILURES ===================================
_______ TestFindBotReviews.test_find_bot_reviews_returns_empty_on_error ________

self = <stampbot.github_client.GitHubAppClient object at 0x7febd6f3cfa0>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42

    def find_bot_reviews(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
    ) -> list[int]:
        """Find all reviews created by this bot on a PR.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
    
        Returns:
            List of review IDs created by the bot
        """
        start_time = time.time()
    
        with create_span(
            "github.find_bot_reviews",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
>               repo = client.get_repo(repo_full_name)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:462: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo' id='140650900832672'>
args = ('owner/repo',), kwargs = {}, effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestFindBotReviews object at 0x7febdb3d0e10>

    def test_find_bot_reviews_returns_empty_on_error(self):
        """Test that find_bot_reviews returns empty list on error."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_github = Mock()
            mock_github.get_repo.side_effect = Exception("API Error")
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.find_bot_reviews(123456, "owner/repo", 42)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:640: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7febd6f3cfa0>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42

    def find_bot_reviews(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
    ) -> list[int]:
        """Find all reviews created by this bot on a PR.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
    
        Returns:
            List of review IDs created by the bot
        """
        start_time = time.time()
    
        with create_span(
            "github.find_bot_reviews",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                # Get bot user via JWT-authenticated integration (installation tokens
                # cannot call GET /user, which is restricted by GitHub Apps integration)
                app_info = self.integration.get_app()
                bot_user = f"{app_info.slug}[bot]"
    
                # Find all reviews by bot that are approvals
                bot_review_ids = []
                for review in pr.get_reviews():
                    if review.user.login == bot_user and review.state == "APPROVED":
                        bot_review_ids.append(review.id)
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="find_reviews").observe(
                    duration
                )
                github_api_requests_total.labels(operation="find_reviews", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(
                    span, {"github.reviews_found": len(bot_review_ids), "github.bot_user": bot_user}
                )
                set_span_ok(span)
    
                return bot_review_ids
    
            except Exception as e:
>               duration = time.time() >> start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for >>: 'float' and 'float'

stampbot/github_client.py:492: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestFindBotReviews::test_find_bot_reviews_returns_empty_on_error - TypeError: unsupported operand type(s) for >>: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 59 passed, 3 deselected in 0.88s
operator: core/ReplaceBinaryOperator_Sub_RShift, occurrence: 11
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -546,7 +546,7 @@
 
                 pr.create_review(body=message, event="COMMENT")
 
-                duration = time.time() - start_time
+                duration = time.time() >> start_time
                 github_api_request_duration_seconds.labels(operation="comment").observe(duration)
                 github_api_requests_total.labels(operation="comment", status="success").inc()
 
........................................................................ [ 34%]
.F
=================================== FAILURES ===================================
_______ TestCreatePrReviewComment.test_create_pr_review_comment_success ________

self = <tests.test_github_client.TestCreatePrReviewComment object at 0x7f427aa2d6d0>

    def test_create_pr_review_comment_success(self):
        """Test successful review comment creation."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_pr = Mock()
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=1, limit=2))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.create_pr_review_comment(
                123456,
                "owner/repo",
                42,
                "Config error",
            )
    
>           assert result is True
E           assert False is True

tests/test_github_client.py:1213: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:57:47 [warning  ] Failed to post config error review for PR #42 in owner/repo: unsupported operand type(s) for >>: 'float' and 'float' extra={'repo': 'owner/repo', 'pr_number': 42, 'installation_id': 123456, 'error': "unsupported operand type(s) for >>: 'float' and 'float'"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestCreatePrReviewComment::test_create_pr_review_comment_success - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 73 passed, 3 deselected in 0.79s
operator: core/ReplaceBinaryOperator_Sub_RShift, occurrence: 12
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -568,7 +568,7 @@
                 return True
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() >> start_time
                 github_api_request_duration_seconds.labels(operation="comment").observe(duration)
                 github_api_requests_total.labels(operation="comment", status="failure").inc()
 
........................................................................ [ 34%]
..F
=================================== FAILURES ===================================
_______ TestCreatePrReviewComment.test_create_pr_review_comment_failure ________

self = <stampbot.github_client.GitHubAppClient object at 0x7fb05a7cadb0>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
message = 'Config error'

    def create_pr_review_comment(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        message: str,
    ) -> bool:
        """Create a comment review on a pull request.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            message: Review comment body
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.create_review_comment",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
>               pr = repo.get_pull(pr_number)
                     ^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:545: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_pull' id='140395409050320'>
args = (42,), kwargs = {}, effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestCreatePrReviewComment object at 0x7fb05e945810>

    def test_create_pr_review_comment_failure(self):
        """Test review comment creation handles errors."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_pull.side_effect = Exception("API Error")
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.create_pr_review_comment(
                123456,
                "owner/repo",
                42,
                "Config error",
            )

tests/test_github_client.py:1248: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7fb05a7cadb0>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
message = 'Config error'

    def create_pr_review_comment(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        message: str,
    ) -> bool:
        """Create a comment review on a pull request.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            message: Review comment body
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.create_review_comment",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                pr.create_review(body=message, event="COMMENT")
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="comment").observe(duration)
                github_api_requests_total.labels(operation="comment", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.result": "commented"})
                set_span_ok(span)
    
                logger.info(
                    "Posted config error review on PR #%d in %s",
                    pr_number,
                    repo_full_name,
                    extra={
                        "repo": repo_full_name,
                        "pr_number": pr_number,
                        "installation_id": installation_id,
                    },
                )
                return True
    
            except Exception as e:
>               duration = time.time() >> start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for >>: 'float' and 'float'

stampbot/github_client.py:571: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestCreatePrReviewComment::test_create_pr_review_comment_failure - TypeError: unsupported operand type(s) for >>: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 74 passed, 3 deselected in 0.93s
operator: core/ReplaceBinaryOperator_Sub_RShift, occurrence: 13
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -619,7 +619,7 @@
                 repo = client.get_repo(repo_full_name)
                 repo.get_label(label_name)
 
-                duration = time.time() - start_time
+                duration = time.time() >> start_time
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="success").inc()
 
.....................................................................F
=================================== FAILURES ===================================
__________________ TestRepoHasLabel.test_repo_has_label_true ___________________

self = <tests.test_github_client.TestRepoHasLabel object at 0x7f1dc3145450>

    def test_repo_has_label_true(self):
        """Test repo_has_label returns True when label exists."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_label.return_value = Mock()
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=1, limit=2))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.repo_has_label(123456, "owner/repo", "autoapprove")
    
>           assert result is True
E           assert None is True

tests/test_github_client.py:1054: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:26:46 [debug    ] Could not verify label autoapprove in owner/repo: unsupported operand type(s) for >>: 'float' and 'float' extra={'repo': 'owner/repo', 'label': 'autoapprove', 'installation_id': 123456, 'error': "unsupported operand type(s) for >>: 'float' and 'float'"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestRepoHasLabel::test_repo_has_label_true - assert None is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 69 passed, 3 deselected in 0.77s
operator: core/ReplaceBinaryOperator_Sub_RShift, occurrence: 14
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -630,7 +630,7 @@
                 return True
 
             except GithubException as e:
-                duration = time.time() - start_time
+                duration = time.time() >> start_time
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="failure").inc()
 
......................................................................F
=================================== FAILURES ===================================
________________ TestRepoHasLabel.test_repo_has_label_not_found ________________

self = <stampbot.github_client.GitHubAppClient object at 0x7f4f63f51730>
installation_id = 123456, repo_full_name = 'owner/repo', label_name = 'missing'

    def repo_has_label(
        self,
        installation_id: int,
        repo_full_name: str,
        label_name: str,
    ) -> bool | None:
        """Check whether a repository has a label.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            label_name: Label name to look up
    
        Returns:
            True if label exists, False if not found, None if unknown due to error
        """
        start_time = time.time()
    
        with create_span(
            "github.get_label",
            {
                "github.repo": repo_full_name,
                "github.label": label_name,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
>               repo.get_label(label_name)

stampbot/github_client.py:620: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_label' id='139978955974576'>
args = ('missing',), kwargs = {}
effect = GithubException(404 {"message": "Not Found"})

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               github.GithubException.GithubException: 404 {"message": "Not Found"}

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: GithubException

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestRepoHasLabel object at 0x7f4f6c20d590>

    def test_repo_has_label_not_found(self):
        """Test repo_has_label returns False when label is missing."""
        from github.GithubException import GithubException
    
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_label.side_effect = GithubException(404, {"message": "Not Found"}, None)
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.repo_has_label(123456, "owner/repo", "missing")
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:1090: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f4f63f51730>
installation_id = 123456, repo_full_name = 'owner/repo', label_name = 'missing'

    def repo_has_label(
        self,
        installation_id: int,
        repo_full_name: str,
        label_name: str,
    ) -> bool | None:
        """Check whether a repository has a label.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            label_name: Label name to look up
    
        Returns:
            True if label exists, False if not found, None if unknown due to error
        """
        start_time = time.time()
    
        with create_span(
            "github.get_label",
            {
                "github.repo": repo_full_name,
                "github.label": label_name,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                repo.get_label(label_name)
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                github_api_requests_total.labels(operation="get_label", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.label_found": True})
                set_span_ok(span)
                return True
    
            except GithubException as e:
>               duration = time.time() >> start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for >>: 'float' and 'float'

stampbot/github_client.py:633: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestRepoHasLabel::test_repo_has_label_not_found - TypeError: unsupported operand type(s) for >>: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 70 passed, 3 deselected in 0.90s
operator: core/ReplaceBinaryOperator_Sub_RShift, occurrence: 15
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -655,7 +655,7 @@
                 return None
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() >> start_time
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="failure").inc()
 
........................................................................ [ 34%]
F
=================================== FAILURES ===================================
__________________ TestRepoHasLabel.test_repo_has_label_error __________________

self = <stampbot.github_client.GitHubAppClient object at 0x7f0ca84a6a50>
installation_id = 123456, repo_full_name = 'owner/repo'
label_name = 'autoapprove'

    def repo_has_label(
        self,
        installation_id: int,
        repo_full_name: str,
        label_name: str,
    ) -> bool | None:
        """Check whether a repository has a label.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            label_name: Label name to look up
    
        Returns:
            True if label exists, False if not found, None if unknown due to error
        """
        start_time = time.time()
    
        with create_span(
            "github.get_label",
            {
                "github.repo": repo_full_name,
                "github.label": label_name,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
>               repo.get_label(label_name)

stampbot/github_client.py:620: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_label' id='139692339680592'>
args = ('autoapprove',), kwargs = {}, effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestRepoHasLabel object at 0x7f0cac812060>

    def test_repo_has_label_error(self):
        """Test repo_has_label returns None on error."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_label.side_effect = Exception("API Error")
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.repo_has_label(123456, "owner/repo", "autoapprove")
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:1164: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f0ca84a6a50>
installation_id = 123456, repo_full_name = 'owner/repo'
label_name = 'autoapprove'

    def repo_has_label(
        self,
        installation_id: int,
        repo_full_name: str,
        label_name: str,
    ) -> bool | None:
        """Check whether a repository has a label.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            label_name: Label name to look up
    
        Returns:
            True if label exists, False if not found, None if unknown due to error
        """
        start_time = time.time()
    
        with create_span(
            "github.get_label",
            {
                "github.repo": repo_full_name,
                "github.label": label_name,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                repo.get_label(label_name)
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                github_api_requests_total.labels(operation="get_label", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.label_found": True})
                set_span_ok(span)
                return True
    
            except GithubException as e:
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                github_api_requests_total.labels(operation="get_label", status="failure").inc()
    
                if e.status == 404:
                    add_span_attributes(span, {"github.label_found": False})
                    set_span_ok(span)
                    return False
    
                set_span_error(span, e)
                logger.debug(
                    "Could not verify label %s in %s: %s",
                    label_name,
                    repo_full_name,
                    _sanitize_error(e),
                    extra={
                        "repo": repo_full_name,
                        "label": label_name,
                        "installation_id": installation_id,
                        "error": _sanitize_error(e),
                    },
                )
                return None
    
            except Exception as e:
>               duration = time.time() >> start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for >>: 'float' and 'float'

stampbot/github_client.py:658: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestRepoHasLabel::test_repo_has_label_error - TypeError: unsupported operand type(s) for >>: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 72 passed, 3 deselected in 0.89s
operator: core/ReplaceBinaryOperator_Sub_RShift, occurrence: 16
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -718,7 +718,7 @@
 
                 has_permission = permission_index >= required_index
 
-                duration = time.time() - start_time
+                duration = time.time() >> start_time
                 github_api_request_duration_seconds.labels(operation="get_permission").observe(
                     duration
                 )
............................................................F
=================================== FAILURES ===================================
_____________ TestUserHasPermission.test_user_has_permission_true ______________

self = <tests.test_github_client.TestUserHasPermission object at 0x7f92d7b6cf50>

    def test_user_has_permission_true(self):
        """Test required permission satisfied returns True."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_collaborator_permission.return_value = "write"
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=1, limit=2))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.user_has_permission(123456, "owner/repo", "alice", "write")
    
>           assert result is True
E           assert False is True

tests/test_github_client.py:683: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 14:00:34 [warning  ] Failed to check collaborator permission for alice in owner/repo: unsupported operand type(s) for >>: 'float' and 'float' extra={'repo': 'owner/repo', 'username': 'alice', 'installation_id': 123456, 'error': "unsupported operand type(s) for >>: 'float' and 'float'"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestUserHasPermission::test_user_has_permission_true - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 60 passed, 3 deselected in 0.76s
operator: core/ReplaceBinaryOperator_Sub_RShift, occurrence: 17
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -738,7 +738,7 @@
                 return has_permission
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() >> start_time
                 github_api_request_duration_seconds.labels(operation="get_permission").observe(
                     duration
                 )
..............................................................F
=================================== FAILURES ===================================
_____________ TestUserHasPermission.test_user_has_permission_error _____________

self = <stampbot.github_client.GitHubAppClient object at 0x7fdf14c4bf50>
installation_id = 123456, repo_full_name = 'owner/repo', username = 'carol'
required_permission = 'maintain'

    def user_has_permission(
        self,
        installation_id: int,
        repo_full_name: str,
        username: str,
        required_permission: str,
    ) -> bool:
        """Check whether a user has required access to a repository.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            username: GitHub username to check
            required_permission: Minimum required repository permission
    
        Returns:
            True if user meets or exceeds required permission, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.get_collaborator_permission",
            {
                "github.repo": repo_full_name,
                "github.username": username,
                "github.required_permission": required_permission,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
    
>               permission = repo.get_collaborator_permission(username)
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:710: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_collaborator_permission' id='140596102826704'>
args = ('carol',), kwargs = {}, effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestUserHasPermission object at 0x7fdf19119a70>

    def test_user_has_permission_error(self):
        """Test permission check returns False on error."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_collaborator_permission.side_effect = Exception("API Error")
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.user_has_permission(123456, "owner/repo", "carol", "maintain")
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:754: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7fdf14c4bf50>
installation_id = 123456, repo_full_name = 'owner/repo', username = 'carol'
required_permission = 'maintain'

    def user_has_permission(
        self,
        installation_id: int,
        repo_full_name: str,
        username: str,
        required_permission: str,
    ) -> bool:
        """Check whether a user has required access to a repository.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            username: GitHub username to check
            required_permission: Minimum required repository permission
    
        Returns:
            True if user meets or exceeds required permission, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.get_collaborator_permission",
            {
                "github.repo": repo_full_name,
                "github.username": username,
                "github.required_permission": required_permission,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
    
                permission = repo.get_collaborator_permission(username)
                permission_order = ["none", "read", "triage", "write", "maintain", "admin"]
                try:
                    permission_index = permission_order.index(permission)
                    required_index = permission_order.index(required_permission)
                except ValueError:
                    permission_index = -1
                    required_index = 99
    
                has_permission = permission_index >= required_index
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_permission").observe(
                    duration
                )
                github_api_requests_total.labels(operation="get_permission", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(
                    span,
                    {
                        "github.permission": permission,
                        "github.has_permission": has_permission,
                    },
                )
                set_span_ok(span)
    
                return has_permission
    
            except Exception as e:
>               duration = time.time() >> start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for >>: 'float' and 'float'

stampbot/github_client.py:741: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestUserHasPermission::test_user_has_permission_error - TypeError: unsupported operand type(s) for >>: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 62 passed, 3 deselected in 0.89s
operator: core/ReplaceBinaryOperator_Sub_RShift, occurrence: 18
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -829,7 +829,7 @@
                         )
                         continue
 
-                duration = time.time() - start_time
+                duration = time.time() >> start_time
                 github_api_request_duration_seconds.labels(operation="get_user_teams").observe(
                     duration
                 )
................................................................F
=================================== FAILURES ===================================
____________ TestGetUserTeamSlugs.test_get_user_team_slugs_success _____________

self = <tests.test_github_client.TestGetUserTeamSlugs object at 0x7f4cd5d951d0>

    def test_get_user_team_slugs_success(self):
        """Test successful team membership check."""
    
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            # Mock user
            mock_user = Mock()
            mock_user.login = "alice"
    
            # Mock team that has alice as member
            mock_team = Mock()
            mock_team.has_in_members.return_value = True
    
            # Mock org
            mock_org = Mock()
            mock_org.get_team_by_slug.return_value = mock_team
    
            mock_github = Mock()
            mock_github.get_organization.return_value = mock_org
            mock_github.get_user.return_value = mock_user
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.get_user_team_slugs(
                123456, "acme", "alice", ["release-team", "deploy-team"]
            )
    
>           assert result == ["release-team", "deploy-team"]
E           AssertionError: assert [] == ['release-tea...'deploy-team']
E             
E             Right contains 2 more items, first extra item: 'release-team'
E             
E             Full diff:
E             + []
E             - [
E             -     'release-team',
E             -     'deploy-team',
E             - ]

tests/test_github_client.py:848: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 14:11:21 [debug    ] User alice is a member of team release-team extra={'org': 'acme', 'username': 'alice', 'team': 'release-team'}
2026-03-27 14:11:21 [debug    ] User alice is a member of team deploy-team extra={'org': 'acme', 'username': 'alice', 'team': 'deploy-team'}
2026-03-27 14:11:21 [warning  ] Failed to check team memberships for alice in acme: unsupported operand type(s) for >>: 'float' and 'float' extra={'org': 'acme', 'username': 'alice', 'installation_id': 123456, 'error': "unsupported operand type(s) for >>: 'float' and 'float'"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetUserTeamSlugs::test_get_user_team_slugs_success - AssertionError: assert [] == ['release-tea...'deploy-team']
  
  Right contains 2 more items, first extra item: 'release-team'
  
  Full diff:
  + []
  - [
  -     'release-team',
  -     'deploy-team',
  - ]
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 64 passed, 3 deselected in 0.76s
operator: core/ReplaceBinaryOperator_Sub_RShift, occurrence: 19
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -849,7 +849,7 @@
                 return member_teams
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() >> start_time
                 github_api_request_duration_seconds.labels(operation="get_user_teams").observe(
                     duration
                 )
....................................................................F
=================================== FAILURES ===================================
_______ TestGetUserTeamSlugs.test_get_user_team_slugs_general_exception ________

self = <stampbot.github_client.GitHubAppClient object at 0x7f6bdb437350>
installation_id = 123456, org_name = 'acme', username = 'alice'
allowed_teams = ['release-team']

    def get_user_team_slugs(
        self,
        installation_id: int,
        org_name: str,
        username: str,
        allowed_teams: list[str],
    ) -> list[str]:
        """Get team slugs the user is a member of from the allowed teams list.
    
        Args:
            installation_id: GitHub App installation ID
            org_name: Organization name
            username: GitHub username to check
            allowed_teams: List of team slugs to check (can be "org/team" or just "team")
    
        Returns:
            List of team slugs the user is a member of
        """
        start_time = time.time()
    
        with create_span(
            "github.get_user_team_slugs",
            {
                "github.org": org_name,
                "github.username": username,
                "github.installation_id": installation_id,
                "github.teams_to_check": len(allowed_teams),
            },
        ) as span:
            member_teams: list[str] = []
    
            try:
                client = self._get_installation_client(installation_id)
>               org = client.get_organization(org_name)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:796: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_organization' id='140101218678640'>
args = ('acme',), kwargs = {}, effect = Exception('Network error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: Network error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestGetUserTeamSlugs object at 0x7f6bdfb4c5f0>

    def test_get_user_team_slugs_general_exception(self):
        """Test team membership check with general exception."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_github = Mock()
            mock_github.get_organization.side_effect = Exception("Network error")
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.get_user_team_slugs(123456, "acme", "alice", ["release-team"])
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:1010: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f6bdb437350>
installation_id = 123456, org_name = 'acme', username = 'alice'
allowed_teams = ['release-team']

    def get_user_team_slugs(
        self,
        installation_id: int,
        org_name: str,
        username: str,
        allowed_teams: list[str],
    ) -> list[str]:
        """Get team slugs the user is a member of from the allowed teams list.
    
        Args:
            installation_id: GitHub App installation ID
            org_name: Organization name
            username: GitHub username to check
            allowed_teams: List of team slugs to check (can be "org/team" or just "team")
    
        Returns:
            List of team slugs the user is a member of
        """
        start_time = time.time()
    
        with create_span(
            "github.get_user_team_slugs",
            {
                "github.org": org_name,
                "github.username": username,
                "github.installation_id": installation_id,
                "github.teams_to_check": len(allowed_teams),
            },
        ) as span:
            member_teams: list[str] = []
    
            try:
                client = self._get_installation_client(installation_id)
                org = client.get_organization(org_name)
    
                for team_ref in allowed_teams:
                    # Extract team slug (handle both "org/team" and "team" formats)
                    team_slug = team_ref.split("/")[-1] if "/" in team_ref else team_ref
    
                    try:
                        team = org.get_team_by_slug(team_slug)
                        # Check if user is a member
                        user = client.get_user(username)
                        if team.has_in_members(user):  # type: ignore[arg-type]
                            member_teams.append(team_slug)
                            logger.debug(
                                "User %s is a member of team %s",
                                username,
                                team_slug,
                                extra={
                                    "org": org_name,
                                    "username": username,
                                    "team": team_slug,
                                },
                            )
                    except GithubException as e:
                        # Team not found or no access - skip silently
                        logger.debug(
                            "Could not check team %s membership: %s",
                            team_slug,
                            e.data.get("message", str(e)) if hasattr(e, "data") else str(e),
                            extra={
                                "org": org_name,
                                "team": team_slug,
                                "username": username,
                            },
                        )
                        continue
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_user_teams").observe(
                    duration
                )
                github_api_requests_total.labels(operation="get_user_teams", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(
                    span,
                    {
                        "github.member_teams": len(member_teams),
                        "github.teams_checked": len(allowed_teams),
                    },
                )
                set_span_ok(span)
    
                return member_teams
    
            except Exception as e:
>               duration = time.time() >> start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for >>: 'float' and 'float'

stampbot/github_client.py:852: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetUserTeamSlugs::test_get_user_team_slugs_general_exception - TypeError: unsupported operand type(s) for >>: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 68 passed, 3 deselected in 0.90s
operator: core/ReplaceBinaryOperator_Sub_LShift, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -168,7 +168,7 @@
                     retry=retry,
                 )
 
-                duration = time.time() - start_time
+                duration = time.time() << start_time
                 github_api_request_duration_seconds.labels(operation="get_token").observe(duration)
                 github_api_requests_total.labels(operation="get_token", status="success").inc()
 
...............................................F
=================================== FAILURES ===================================
________ TestGetInstallationClient.test_get_installation_client_success ________

self = <tests.test_github_client.TestGetInstallationClient object at 0x7f40cd9ec050>

    def test_get_installation_client_success(self):
        """Test successful installation client creation."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client._get_installation_client(123456)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:211: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f40c96a0830>
installation_id = 123456

    def _get_installation_client(self, installation_id: int) -> Github:
        """Get authenticated GitHub client for an installation.
    
        Args:
            installation_id: GitHub App installation ID.
    
        Returns:
            Authenticated Github client instance with timeout and retry configured.
    
        Raises:
            github.GithubException: If token exchange fails.
        """
        start_time = time.time()
    
        with create_span(
            "github.get_installation_token",
            {"github.installation_id": installation_id},
        ) as span:
            try:
                auth = self.integration.get_access_token(installation_id)
    
                # Configure retry with exponential backoff
                retry = Retry(
                    total=GITHUB_API_RETRY_TOTAL,
                    backoff_factor=GITHUB_API_RETRY_BACKOFF,
                    status_forcelist=[500, 502, 503, 504],
                    allowed_methods=["GET", "POST", "PUT", "DELETE", "PATCH"],
                )
    
                client = Github(
                    auth=Auth.Token(auth.token),
                    timeout=GITHUB_API_TIMEOUT,
                    retry=retry,
                )
    
>               duration = time.time() << start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for <<: 'float' and 'float'

stampbot/github_client.py:171: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetInstallationClient::test_get_installation_client_success - TypeError: unsupported operand type(s) for <<: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 47 passed, 3 deselected in 0.77s
operator: core/ReplaceBinaryOperator_Sub_LShift, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -176,7 +176,7 @@
                 return client
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() << start_time
                 github_api_request_duration_seconds.labels(operation="get_token").observe(duration)
                 github_api_requests_total.labels(operation="get_token", status="failure").inc()
                 set_span_error(span, e)
................................................F
=================================== FAILURES ===================================
________ TestGetInstallationClient.test_get_installation_client_failure ________

self = <stampbot.github_client.GitHubAppClient object at 0x7fa000dd1230>
installation_id = 123456

    def _get_installation_client(self, installation_id: int) -> Github:
        """Get authenticated GitHub client for an installation.
    
        Args:
            installation_id: GitHub App installation ID.
    
        Returns:
            Authenticated Github client instance with timeout and retry configured.
    
        Raises:
            github.GithubException: If token exchange fails.
        """
        start_time = time.time()
    
        with create_span(
            "github.get_installation_token",
            {"github.installation_id": installation_id},
        ) as span:
            try:
>               auth = self.integration.get_access_token(installation_id)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:155: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='GithubIntegration().get_access_token' id='140325166562816'>
args = (123456,), kwargs = {}, effect = Exception('Token exchange failed')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: Token exchange failed

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestGetInstallationClient object at 0x7fa003e88190>

    def test_get_installation_client_failure(self):
        """Test installation client creation failure."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_integration.get_access_token.side_effect = Exception("Token exchange failed")
            mock_integration_cls.return_value = mock_integration
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            with pytest.raises(Exception, match="Token exchange failed"):
>               client._get_installation_client(123456)

tests/test_github_client.py:240: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7fa000dd1230>
installation_id = 123456

    def _get_installation_client(self, installation_id: int) -> Github:
        """Get authenticated GitHub client for an installation.
    
        Args:
            installation_id: GitHub App installation ID.
    
        Returns:
            Authenticated Github client instance with timeout and retry configured.
    
        Raises:
            github.GithubException: If token exchange fails.
        """
        start_time = time.time()
    
        with create_span(
            "github.get_installation_token",
            {"github.installation_id": installation_id},
        ) as span:
            try:
                auth = self.integration.get_access_token(installation_id)
    
                # Configure retry with exponential backoff
                retry = Retry(
                    total=GITHUB_API_RETRY_TOTAL,
                    backoff_factor=GITHUB_API_RETRY_BACKOFF,
                    status_forcelist=[500, 502, 503, 504],
                    allowed_methods=["GET", "POST", "PUT", "DELETE", "PATCH"],
                )
    
                client = Github(
                    auth=Auth.Token(auth.token),
                    timeout=GITHUB_API_TIMEOUT,
                    retry=retry,
                )
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_token").observe(duration)
                github_api_requests_total.labels(operation="get_token", status="success").inc()
    
                set_span_ok(span)
                return client
    
            except Exception as e:
>               duration = time.time() << start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for <<: 'float' and 'float'

stampbot/github_client.py:179: TypeError

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestGetInstallationClient object at 0x7fa003e88190>

    def test_get_installation_client_failure(self):
        """Test installation client creation failure."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_integration.get_access_token.side_effect = Exception("Token exchange failed")
            mock_integration_cls.return_value = mock_integration
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           with pytest.raises(Exception, match="Token exchange failed"):
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E           AssertionError: Regex pattern did not match.
E             Expected regex: 'Token exchange failed'
E             Actual message: "unsupported operand type(s) for <<: 'float' and 'float'"

tests/test_github_client.py:239: AssertionError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetInstallationClient::test_get_installation_client_failure - AssertionError: Regex pattern did not match.
  Expected regex: 'Token exchange failed'
  Actual message: "unsupported operand type(s) for <<: 'float' and 'float'"
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 48 passed, 3 deselected in 0.87s
operator: core/ReplaceBinaryOperator_Sub_LShift, occurrence: 2
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -240,7 +240,7 @@
                     event="APPROVE",
                 )
 
-                duration = time.time() - start_time
+                duration = time.time() << start_time
                 github_api_request_duration_seconds.labels(operation="approve").observe(duration)
                 github_api_requests_total.labels(operation="approve", status="success").inc()
 
...................................................F
=================================== FAILURES ===================================
____________________ TestApprovePR.test_approve_pr_success _____________________

self = <tests.test_github_client.TestApprovePR object at 0x7f042c4fc550>

    def test_approve_pr_success(self):
        """Test successful PR approval."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_pr = Mock()
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.approve_pr(123456, "owner/repo", 42)
    
>           assert result is True
E           assert False is True

tests/test_github_client.py:313: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:48:31 [error    ] Failed to approve PR #42 in owner/repo: unsupported operand type(s) for <<: 'float' and 'float' extra={'repo': 'owner/repo', 'pr_number': 42, 'installation_id': 123456, 'error': "unsupported operand type(s) for <<: 'float' and 'float'"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestApprovePR::test_approve_pr_success - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 51 passed, 3 deselected in 0.73s
operator: core/ReplaceBinaryOperator_Sub_LShift, occurrence: 3
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -260,7 +260,7 @@
                 return True
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() << start_time
                 github_api_request_duration_seconds.labels(operation="approve").observe(duration)
                 github_api_requests_total.labels(operation="approve", status="failure").inc()
 
....................................................F
=================================== FAILURES ===================================
____________________ TestApprovePR.test_approve_pr_failure _____________________

self = <stampbot.github_client.GitHubAppClient object at 0x7f8fbe376c50>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
comment = 'Auto-approved by Stampbot'

    def approve_pr(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        comment: str = "Auto-approved by Stampbot",
    ) -> bool:
        """Approve a pull request.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            comment: Review comment
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.approve_pr",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                # Create approval review
>               pr.create_review(
                    body=comment,
                    event="APPROVE",
                )

stampbot/github_client.py:238: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_pull().create_review' id='140255348766912'>
args = (), kwargs = {'body': 'Auto-approved by Stampbot', 'event': 'APPROVE'}
effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestApprovePR object at 0x7f8fc2698690>

    def test_approve_pr_failure(self):
        """Test PR approval failure."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_pr = Mock()
            mock_pr.create_review.side_effect = Exception("API Error")
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.approve_pr(123456, "owner/repo", 42)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:350: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f8fbe376c50>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
comment = 'Auto-approved by Stampbot'

    def approve_pr(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        comment: str = "Auto-approved by Stampbot",
    ) -> bool:
        """Approve a pull request.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            comment: Review comment
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.approve_pr",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                # Create approval review
                pr.create_review(
                    body=comment,
                    event="APPROVE",
                )
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="approve").observe(duration)
                github_api_requests_total.labels(operation="approve", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.result": "approved"})
                set_span_ok(span)
    
                logger.info(
                    f"Approved PR #{pr_number} in {repo_full_name}",
                    extra={
                        "repo": repo_full_name,
                        "pr_number": pr_number,
                        "installation_id": installation_id,
                    },
                )
                return True
    
            except Exception as e:
>               duration = time.time() << start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for <<: 'float' and 'float'

stampbot/github_client.py:263: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestApprovePR::test_approve_pr_failure - TypeError: unsupported operand type(s) for <<: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 52 passed, 3 deselected in 0.86s
operator: core/ReplaceBinaryOperator_Sub_LShift, occurrence: 4
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -317,7 +317,7 @@
                 review = pr.get_review(review_id)
                 review.dismiss(message)
 
-                duration = time.time() - start_time
+                duration = time.time() << start_time
                 github_api_request_duration_seconds.labels(operation="dismiss").observe(duration)
                 github_api_requests_total.labels(operation="dismiss", status="success").inc()
 
.....................................................F
=================================== FAILURES ===================================
______________ TestDismissApproval.test_dismiss_approval_success _______________

self = <tests.test_github_client.TestDismissApproval object at 0x7f169834c7d0>

    def test_dismiss_approval_success(self):
        """Test successful approval dismissal."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_review = Mock()
            mock_pr = Mock()
            mock_pr.get_review.return_value = mock_review
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.dismiss_approval(123456, "owner/repo", 42, 999)
    
>           assert result is True
E           assert False is True

tests/test_github_client.py:396: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 14:08:58 [error    ] Failed to dismiss approval for PR #42 in owner/repo: unsupported operand type(s) for <<: 'float' and 'float' extra={'repo': 'owner/repo', 'pr_number': 42, 'installation_id': 123456, 'error': "unsupported operand type(s) for <<: 'float' and 'float'"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestDismissApproval::test_dismiss_approval_success - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 53 passed, 3 deselected in 0.74s
operator: core/ReplaceBinaryOperator_Sub_LShift, occurrence: 5
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -338,7 +338,7 @@
                 return True
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() << start_time
                 github_api_request_duration_seconds.labels(operation="dismiss").observe(duration)
                 github_api_requests_total.labels(operation="dismiss", status="failure").inc()
 
......................................................F
=================================== FAILURES ===================================
______________ TestDismissApproval.test_dismiss_approval_failure _______________

self = <stampbot.github_client.GitHubAppClient object at 0x7fca1ab16850>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
review_id = 999, message = 'Approval dismissed by Stampbot'

    def dismiss_approval(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        review_id: int,
        message: str = "Approval dismissed by Stampbot",
    ) -> bool:
        """Dismiss a pull request approval.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            review_id: Review ID to dismiss
            message: Dismissal message
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.dismiss_approval",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.review_id": review_id,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                # Get the review and dismiss it
                review = pr.get_review(review_id)
>               review.dismiss(message)

stampbot/github_client.py:318: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_pull().get_review().dismiss' id='140506006844288'>
args = ('Approval dismissed by Stampbot',), kwargs = {}
effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestDismissApproval object at 0x7fca1ef10910>

    def test_dismiss_approval_failure(self):
        """Test approval dismissal failure."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_review = Mock()
            mock_review.dismiss.side_effect = Exception("API Error")
            mock_pr = Mock()
            mock_pr.get_review.return_value = mock_review
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.dismiss_approval(123456, "owner/repo", 42, 999)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:435: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7fca1ab16850>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
review_id = 999, message = 'Approval dismissed by Stampbot'

    def dismiss_approval(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        review_id: int,
        message: str = "Approval dismissed by Stampbot",
    ) -> bool:
        """Dismiss a pull request approval.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            review_id: Review ID to dismiss
            message: Dismissal message
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.dismiss_approval",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.review_id": review_id,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                # Get the review and dismiss it
                review = pr.get_review(review_id)
                review.dismiss(message)
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="dismiss").observe(duration)
                github_api_requests_total.labels(operation="dismiss", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.result": "dismissed"})
                set_span_ok(span)
    
                logger.info(
                    f"Dismissed approval for PR #{pr_number} in {repo_full_name}",
                    extra={
                        "repo": repo_full_name,
                        "pr_number": pr_number,
                        "installation_id": installation_id,
                        "review_id": review_id,
                    },
                )
                return True
    
            except Exception as e:
>               duration = time.time() << start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for <<: 'float' and 'float'

stampbot/github_client.py:341: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestDismissApproval::test_dismiss_approval_failure - TypeError: unsupported operand type(s) for <<: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 54 passed, 3 deselected in 0.86s
operator: core/ReplaceBinaryOperator_Sub_LShift, occurrence: 6
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -393,7 +393,7 @@
 
                 content = repo.get_contents(file_path, ref=ref)  # type: ignore[arg-type]
                 if isinstance(content, list):
-                    duration = time.time() - start_time
+                    duration = time.time() << start_time
                     github_api_request_duration_seconds.labels(operation="get_file").observe(
                         duration
                     )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_LShift, occurrence: 7
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -402,7 +402,7 @@
                     set_span_ok(span)
                     return None
 
-                duration = time.time() - start_time
+                duration = time.time() << start_time
                 github_api_request_duration_seconds.labels(operation="get_file").observe(duration)
                 github_api_requests_total.labels(operation="get_file", status="success").inc()
 
.......................................................F
=================================== FAILURES ===================================
__________________ TestGetRepoFile.test_get_repo_file_success __________________

self = <tests.test_github_client.TestGetRepoFile object at 0x7f1fc0ee4a50>

    def test_get_repo_file_success(self):
        """Test successful file retrieval."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_content = Mock()
            mock_content.decoded_content = b"file content"
            mock_repo = Mock()
            mock_repo.get_contents.return_value = mock_content
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.get_repo_file(123456, "owner/repo", "stampbot.toml")
    
>           assert result == "file content"
E           AssertionError: assert None == 'file content'

tests/test_github_client.py:480: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:48:58 [debug    ] Could not fetch stampbot.toml from owner/repo: unsupported operand type(s) for <<: 'float' and 'float' extra={'repo': 'owner/repo', 'file_path': 'stampbot.toml', 'installation_id': 123456}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetRepoFile::test_get_repo_file_success - AssertionError: assert None == 'file content'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 55 passed, 3 deselected in 0.74s
operator: core/ReplaceBinaryOperator_Sub_LShift, occurrence: 8
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -414,7 +414,7 @@
                 return content.decoded_content.decode("utf-8")
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() << start_time
                 github_api_request_duration_seconds.labels(operation="get_file").observe(duration)
                 github_api_requests_total.labels(operation="get_file", status="not_found").inc()
 
.........................................................F
=================================== FAILURES ===================================
___________ TestGetRepoFile.test_get_repo_file_returns_none_on_error ___________

self = <stampbot.github_client.GitHubAppClient object at 0x7f839c658cd0>
installation_id = 123456, repo_full_name = 'owner/repo'
file_path = 'nonexistent.toml', ref = None

    def get_repo_file(
        self,
        installation_id: int,
        repo_full_name: str,
        file_path: str,
        ref: str | None = None,
    ) -> str | None:
        """Get file content from repository.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            file_path: Path to file in repository
            ref: Git reference (branch, tag, commit). Defaults to default branch
    
        Returns:
            File content as string, or None if not found
        """
        start_time = time.time()
    
        with create_span(
            "github.get_file",
            {
                "github.repo": repo_full_name,
                "github.file_path": file_path,
                "github.ref": ref or "default",
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
    
>               content = repo.get_contents(file_path, ref=ref)  # type: ignore[arg-type]
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:394: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_contents' id='140203241653648'>
args = ('nonexistent.toml',), kwargs = {'ref': None}
effect = Exception('File not found')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: File not found

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestGetRepoFile object at 0x7f83a07cd940>

    def test_get_repo_file_returns_none_on_error(self):
        """Test that get_repo_file returns None when file not found."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_contents.side_effect = Exception("File not found")
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.get_repo_file(123456, "owner/repo", "nonexistent.toml")
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:551: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f839c658cd0>
installation_id = 123456, repo_full_name = 'owner/repo'
file_path = 'nonexistent.toml', ref = None

    def get_repo_file(
        self,
        installation_id: int,
        repo_full_name: str,
        file_path: str,
        ref: str | None = None,
    ) -> str | None:
        """Get file content from repository.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            file_path: Path to file in repository
            ref: Git reference (branch, tag, commit). Defaults to default branch
    
        Returns:
            File content as string, or None if not found
        """
        start_time = time.time()
    
        with create_span(
            "github.get_file",
            {
                "github.repo": repo_full_name,
                "github.file_path": file_path,
                "github.ref": ref or "default",
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
    
                content = repo.get_contents(file_path, ref=ref)  # type: ignore[arg-type]
                if isinstance(content, list):
                    duration = time.time() - start_time
                    github_api_request_duration_seconds.labels(operation="get_file").observe(
                        duration
                    )
                    github_api_requests_total.labels(operation="get_file", status="not_found").inc()
                    add_span_attributes(span, {"github.result": "not_found"})
                    set_span_ok(span)
                    return None
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_file").observe(duration)
                github_api_requests_total.labels(operation="get_file", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.result": "found"})
                set_span_ok(span)
    
                return content.decoded_content.decode("utf-8")
    
            except Exception as e:
>               duration = time.time() << start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for <<: 'float' and 'float'

stampbot/github_client.py:417: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetRepoFile::test_get_repo_file_returns_none_on_error - TypeError: unsupported operand type(s) for <<: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 57 passed, 3 deselected in 0.86s
operator: core/ReplaceBinaryOperator_Sub_LShift, occurrence: 9
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -473,7 +473,7 @@
                     if review.user.login == bot_user and review.state == "APPROVED":
                         bot_review_ids.append(review.id)
 
-                duration = time.time() - start_time
+                duration = time.time() << start_time
                 github_api_request_duration_seconds.labels(operation="find_reviews").observe(
                     duration
                 )
..........................................................F
=================================== FAILURES ===================================
_______________ TestFindBotReviews.test_find_bot_reviews_success _______________

self = <tests.test_github_client.TestFindBotReviews object at 0x7fc9b2988cd0>

    def test_find_bot_reviews_success(self):
        """Test successful bot review finding."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration.get_app.return_value = Mock(slug="stampbot")
            mock_integration_cls.return_value = mock_integration
    
            # Create mock reviews
            bot_review = Mock()
            bot_review.user.login = "stampbot[bot]"
            bot_review.state = "APPROVED"
            bot_review.id = 123
    
            other_review = Mock()
            other_review.user.login = "other-user"
            other_review.state = "APPROVED"
            other_review.id = 456
    
            mock_pr = Mock()
            mock_pr.get_reviews.return_value = [bot_review, other_review]
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.find_bot_reviews(123456, "owner/repo", 42)
    
>           assert result == [123]
E           assert [] == [123]
E             
E             Right contains one more item: 123
E             
E             Full diff:
E             + []
E             - [
E             -     123,
E             - ]

tests/test_github_client.py:608: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:33:09 [error    ] Failed to find bot reviews for PR #42 in owner/repo: unsupported operand type(s) for <<: 'float' and 'float' extra={'repo': 'owner/repo', 'pr_number': 42, 'installation_id': 123456, 'error': "unsupported operand type(s) for <<: 'float' and 'float'"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestFindBotReviews::test_find_bot_reviews_success - assert [] == [123]
  
  Right contains one more item: 123
  
  Full diff:
  + []
  - [
  -     123,
  - ]
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 58 passed, 3 deselected in 0.75s
operator: core/ReplaceBinaryOperator_Sub_LShift, occurrence: 10
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -489,7 +489,7 @@
                 return bot_review_ids
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() << start_time
                 github_api_request_duration_seconds.labels(operation="find_reviews").observe(
                     duration
                 )
...........................................................F
=================================== FAILURES ===================================
_______ TestFindBotReviews.test_find_bot_reviews_returns_empty_on_error ________

self = <stampbot.github_client.GitHubAppClient object at 0x7f58e526cf30>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42

    def find_bot_reviews(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
    ) -> list[int]:
        """Find all reviews created by this bot on a PR.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
    
        Returns:
            List of review IDs created by the bot
        """
        start_time = time.time()
    
        with create_span(
            "github.find_bot_reviews",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
>               repo = client.get_repo(repo_full_name)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:462: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo' id='140019778863520'>
args = ('owner/repo',), kwargs = {}, effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestFindBotReviews object at 0x7f58e91fce10>

    def test_find_bot_reviews_returns_empty_on_error(self):
        """Test that find_bot_reviews returns empty list on error."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_github = Mock()
            mock_github.get_repo.side_effect = Exception("API Error")
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.find_bot_reviews(123456, "owner/repo", 42)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:640: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f58e526cf30>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42

    def find_bot_reviews(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
    ) -> list[int]:
        """Find all reviews created by this bot on a PR.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
    
        Returns:
            List of review IDs created by the bot
        """
        start_time = time.time()
    
        with create_span(
            "github.find_bot_reviews",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                # Get bot user via JWT-authenticated integration (installation tokens
                # cannot call GET /user, which is restricted by GitHub Apps integration)
                app_info = self.integration.get_app()
                bot_user = f"{app_info.slug}[bot]"
    
                # Find all reviews by bot that are approvals
                bot_review_ids = []
                for review in pr.get_reviews():
                    if review.user.login == bot_user and review.state == "APPROVED":
                        bot_review_ids.append(review.id)
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="find_reviews").observe(
                    duration
                )
                github_api_requests_total.labels(operation="find_reviews", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(
                    span, {"github.reviews_found": len(bot_review_ids), "github.bot_user": bot_user}
                )
                set_span_ok(span)
    
                return bot_review_ids
    
            except Exception as e:
>               duration = time.time() << start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for <<: 'float' and 'float'

stampbot/github_client.py:492: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestFindBotReviews::test_find_bot_reviews_returns_empty_on_error - TypeError: unsupported operand type(s) for <<: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 59 passed, 3 deselected in 0.88s
operator: core/ReplaceBinaryOperator_Sub_LShift, occurrence: 11
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -546,7 +546,7 @@
 
                 pr.create_review(body=message, event="COMMENT")
 
-                duration = time.time() - start_time
+                duration = time.time() << start_time
                 github_api_request_duration_seconds.labels(operation="comment").observe(duration)
                 github_api_requests_total.labels(operation="comment", status="success").inc()
 
........................................................................ [ 34%]
.F
=================================== FAILURES ===================================
_______ TestCreatePrReviewComment.test_create_pr_review_comment_success ________

self = <tests.test_github_client.TestCreatePrReviewComment object at 0x7fc0bc1b56d0>

    def test_create_pr_review_comment_success(self):
        """Test successful review comment creation."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_pr = Mock()
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=1, limit=2))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.create_pr_review_comment(
                123456,
                "owner/repo",
                42,
                "Config error",
            )
    
>           assert result is True
E           assert False is True

tests/test_github_client.py:1213: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:51:02 [warning  ] Failed to post config error review for PR #42 in owner/repo: unsupported operand type(s) for <<: 'float' and 'float' extra={'repo': 'owner/repo', 'pr_number': 42, 'installation_id': 123456, 'error': "unsupported operand type(s) for <<: 'float' and 'float'"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestCreatePrReviewComment::test_create_pr_review_comment_success - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 73 passed, 3 deselected in 0.79s
operator: core/ReplaceBinaryOperator_Sub_LShift, occurrence: 12
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -568,7 +568,7 @@
                 return True
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() << start_time
                 github_api_request_duration_seconds.labels(operation="comment").observe(duration)
                 github_api_requests_total.labels(operation="comment", status="failure").inc()
 
........................................................................ [ 34%]
..F
=================================== FAILURES ===================================
_______ TestCreatePrReviewComment.test_create_pr_review_comment_failure ________

self = <stampbot.github_client.GitHubAppClient object at 0x7fb733ab9790>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
message = 'Config error'

    def create_pr_review_comment(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        message: str,
    ) -> bool:
        """Create a comment review on a pull request.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            message: Review comment body
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.create_review_comment",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
>               pr = repo.get_pull(pr_number)
                     ^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:545: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_pull' id='140424822573776'>
args = (42,), kwargs = {}, effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestCreatePrReviewComment object at 0x7fb737d8d810>

    def test_create_pr_review_comment_failure(self):
        """Test review comment creation handles errors."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_pull.side_effect = Exception("API Error")
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.create_pr_review_comment(
                123456,
                "owner/repo",
                42,
                "Config error",
            )

tests/test_github_client.py:1248: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7fb733ab9790>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
message = 'Config error'

    def create_pr_review_comment(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        message: str,
    ) -> bool:
        """Create a comment review on a pull request.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            message: Review comment body
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.create_review_comment",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                pr.create_review(body=message, event="COMMENT")
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="comment").observe(duration)
                github_api_requests_total.labels(operation="comment", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.result": "commented"})
                set_span_ok(span)
    
                logger.info(
                    "Posted config error review on PR #%d in %s",
                    pr_number,
                    repo_full_name,
                    extra={
                        "repo": repo_full_name,
                        "pr_number": pr_number,
                        "installation_id": installation_id,
                    },
                )
                return True
    
            except Exception as e:
>               duration = time.time() << start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for <<: 'float' and 'float'

stampbot/github_client.py:571: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestCreatePrReviewComment::test_create_pr_review_comment_failure - TypeError: unsupported operand type(s) for <<: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 74 passed, 3 deselected in 0.92s
operator: core/ReplaceBinaryOperator_Sub_LShift, occurrence: 13
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -619,7 +619,7 @@
                 repo = client.get_repo(repo_full_name)
                 repo.get_label(label_name)
 
-                duration = time.time() - start_time
+                duration = time.time() << start_time
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="success").inc()
 
.....................................................................F
=================================== FAILURES ===================================
__________________ TestRepoHasLabel.test_repo_has_label_true ___________________

self = <tests.test_github_client.TestRepoHasLabel object at 0x7fa9618f5450>

    def test_repo_has_label_true(self):
        """Test repo_has_label returns True when label exists."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_label.return_value = Mock()
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=1, limit=2))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.repo_has_label(123456, "owner/repo", "autoapprove")
    
>           assert result is True
E           assert None is True

tests/test_github_client.py:1054: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 14:02:56 [debug    ] Could not verify label autoapprove in owner/repo: unsupported operand type(s) for <<: 'float' and 'float' extra={'repo': 'owner/repo', 'label': 'autoapprove', 'installation_id': 123456, 'error': "unsupported operand type(s) for <<: 'float' and 'float'"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestRepoHasLabel::test_repo_has_label_true - assert None is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 69 passed, 3 deselected in 0.77s
operator: core/ReplaceBinaryOperator_Sub_LShift, occurrence: 14
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -630,7 +630,7 @@
                 return True
 
             except GithubException as e:
-                duration = time.time() - start_time
+                duration = time.time() << start_time
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="failure").inc()
 
......................................................................F
=================================== FAILURES ===================================
________________ TestRepoHasLabel.test_repo_has_label_not_found ________________

self = <stampbot.github_client.GitHubAppClient object at 0x7f894844da30>
installation_id = 123456, repo_full_name = 'owner/repo', label_name = 'missing'

    def repo_has_label(
        self,
        installation_id: int,
        repo_full_name: str,
        label_name: str,
    ) -> bool | None:
        """Check whether a repository has a label.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            label_name: Label name to look up
    
        Returns:
            True if label exists, False if not found, None if unknown due to error
        """
        start_time = time.time()
    
        with create_span(
            "github.get_label",
            {
                "github.repo": repo_full_name,
                "github.label": label_name,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
>               repo.get_label(label_name)

stampbot/github_client.py:620: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_label' id='140227599542192'>
args = ('missing',), kwargs = {}
effect = GithubException(404 {"message": "Not Found"})

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               github.GithubException.GithubException: 404 {"message": "Not Found"}

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: GithubException

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestRepoHasLabel object at 0x7f894c611590>

    def test_repo_has_label_not_found(self):
        """Test repo_has_label returns False when label is missing."""
        from github.GithubException import GithubException
    
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_label.side_effect = GithubException(404, {"message": "Not Found"}, None)
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.repo_has_label(123456, "owner/repo", "missing")
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:1090: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f894844da30>
installation_id = 123456, repo_full_name = 'owner/repo', label_name = 'missing'

    def repo_has_label(
        self,
        installation_id: int,
        repo_full_name: str,
        label_name: str,
    ) -> bool | None:
        """Check whether a repository has a label.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            label_name: Label name to look up
    
        Returns:
            True if label exists, False if not found, None if unknown due to error
        """
        start_time = time.time()
    
        with create_span(
            "github.get_label",
            {
                "github.repo": repo_full_name,
                "github.label": label_name,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                repo.get_label(label_name)
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                github_api_requests_total.labels(operation="get_label", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.label_found": True})
                set_span_ok(span)
                return True
    
            except GithubException as e:
>               duration = time.time() << start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for <<: 'float' and 'float'

stampbot/github_client.py:633: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestRepoHasLabel::test_repo_has_label_not_found - TypeError: unsupported operand type(s) for <<: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 70 passed, 3 deselected in 0.89s
operator: core/ReplaceBinaryOperator_Sub_LShift, occurrence: 15
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -655,7 +655,7 @@
                 return None
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() << start_time
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="failure").inc()
 
........................................................................ [ 34%]
F
=================================== FAILURES ===================================
__________________ TestRepoHasLabel.test_repo_has_label_error __________________

self = <stampbot.github_client.GitHubAppClient object at 0x7f578bd6f590>
installation_id = 123456, repo_full_name = 'owner/repo'
label_name = 'autoapprove'

    def repo_has_label(
        self,
        installation_id: int,
        repo_full_name: str,
        label_name: str,
    ) -> bool | None:
        """Check whether a repository has a label.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            label_name: Label name to look up
    
        Returns:
            True if label exists, False if not found, None if unknown due to error
        """
        start_time = time.time()
    
        with create_span(
            "github.get_label",
            {
                "github.repo": repo_full_name,
                "github.label": label_name,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
>               repo.get_label(label_name)

stampbot/github_client.py:620: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_label' id='140013984896336'>
args = ('autoapprove',), kwargs = {}, effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestRepoHasLabel object at 0x7f579446a060>

    def test_repo_has_label_error(self):
        """Test repo_has_label returns None on error."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_label.side_effect = Exception("API Error")
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.repo_has_label(123456, "owner/repo", "autoapprove")
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:1164: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f578bd6f590>
installation_id = 123456, repo_full_name = 'owner/repo'
label_name = 'autoapprove'

    def repo_has_label(
        self,
        installation_id: int,
        repo_full_name: str,
        label_name: str,
    ) -> bool | None:
        """Check whether a repository has a label.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            label_name: Label name to look up
    
        Returns:
            True if label exists, False if not found, None if unknown due to error
        """
        start_time = time.time()
    
        with create_span(
            "github.get_label",
            {
                "github.repo": repo_full_name,
                "github.label": label_name,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                repo.get_label(label_name)
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                github_api_requests_total.labels(operation="get_label", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.label_found": True})
                set_span_ok(span)
                return True
    
            except GithubException as e:
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                github_api_requests_total.labels(operation="get_label", status="failure").inc()
    
                if e.status == 404:
                    add_span_attributes(span, {"github.label_found": False})
                    set_span_ok(span)
                    return False
    
                set_span_error(span, e)
                logger.debug(
                    "Could not verify label %s in %s: %s",
                    label_name,
                    repo_full_name,
                    _sanitize_error(e),
                    extra={
                        "repo": repo_full_name,
                        "label": label_name,
                        "installation_id": installation_id,
                        "error": _sanitize_error(e),
                    },
                )
                return None
    
            except Exception as e:
>               duration = time.time() << start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for <<: 'float' and 'float'

stampbot/github_client.py:658: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestRepoHasLabel::test_repo_has_label_error - TypeError: unsupported operand type(s) for <<: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 72 passed, 3 deselected in 0.93s
operator: core/ReplaceBinaryOperator_Sub_LShift, occurrence: 16
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -718,7 +718,7 @@
 
                 has_permission = permission_index >= required_index
 
-                duration = time.time() - start_time
+                duration = time.time() << start_time
                 github_api_request_duration_seconds.labels(operation="get_permission").observe(
                     duration
                 )
............................................................F
=================================== FAILURES ===================================
_____________ TestUserHasPermission.test_user_has_permission_true ______________

self = <tests.test_github_client.TestUserHasPermission object at 0x7f7370e90f50>

    def test_user_has_permission_true(self):
        """Test required permission satisfied returns True."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_collaborator_permission.return_value = "write"
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=1, limit=2))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.user_has_permission(123456, "owner/repo", "alice", "write")
    
>           assert result is True
E           assert False is True

tests/test_github_client.py:683: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:37:28 [warning  ] Failed to check collaborator permission for alice in owner/repo: unsupported operand type(s) for <<: 'float' and 'float' extra={'repo': 'owner/repo', 'username': 'alice', 'installation_id': 123456, 'error': "unsupported operand type(s) for <<: 'float' and 'float'"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestUserHasPermission::test_user_has_permission_true - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 60 passed, 3 deselected in 0.76s
operator: core/ReplaceBinaryOperator_Sub_LShift, occurrence: 17
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -738,7 +738,7 @@
                 return has_permission
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() << start_time
                 github_api_request_duration_seconds.labels(operation="get_permission").observe(
                     duration
                 )
..............................................................F
=================================== FAILURES ===================================
_____________ TestUserHasPermission.test_user_has_permission_error _____________

self = <stampbot.github_client.GitHubAppClient object at 0x7f893ec533b0>
installation_id = 123456, repo_full_name = 'owner/repo', username = 'carol'
required_permission = 'maintain'

    def user_has_permission(
        self,
        installation_id: int,
        repo_full_name: str,
        username: str,
        required_permission: str,
    ) -> bool:
        """Check whether a user has required access to a repository.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            username: GitHub username to check
            required_permission: Minimum required repository permission
    
        Returns:
            True if user meets or exceeds required permission, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.get_collaborator_permission",
            {
                "github.repo": repo_full_name,
                "github.username": username,
                "github.required_permission": required_permission,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
    
>               permission = repo.get_collaborator_permission(username)
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:710: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_collaborator_permission' id='140227440315088'>
args = ('carol',), kwargs = {}, effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestUserHasPermission object at 0x7f89430f5a70>

    def test_user_has_permission_error(self):
        """Test permission check returns False on error."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_collaborator_permission.side_effect = Exception("API Error")
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.user_has_permission(123456, "owner/repo", "carol", "maintain")
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:754: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f893ec533b0>
installation_id = 123456, repo_full_name = 'owner/repo', username = 'carol'
required_permission = 'maintain'

    def user_has_permission(
        self,
        installation_id: int,
        repo_full_name: str,
        username: str,
        required_permission: str,
    ) -> bool:
        """Check whether a user has required access to a repository.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            username: GitHub username to check
            required_permission: Minimum required repository permission
    
        Returns:
            True if user meets or exceeds required permission, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.get_collaborator_permission",
            {
                "github.repo": repo_full_name,
                "github.username": username,
                "github.required_permission": required_permission,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
    
                permission = repo.get_collaborator_permission(username)
                permission_order = ["none", "read", "triage", "write", "maintain", "admin"]
                try:
                    permission_index = permission_order.index(permission)
                    required_index = permission_order.index(required_permission)
                except ValueError:
                    permission_index = -1
                    required_index = 99
    
                has_permission = permission_index >= required_index
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_permission").observe(
                    duration
                )
                github_api_requests_total.labels(operation="get_permission", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(
                    span,
                    {
                        "github.permission": permission,
                        "github.has_permission": has_permission,
                    },
                )
                set_span_ok(span)
    
                return has_permission
    
            except Exception as e:
>               duration = time.time() << start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for <<: 'float' and 'float'

stampbot/github_client.py:741: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestUserHasPermission::test_user_has_permission_error - TypeError: unsupported operand type(s) for <<: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 62 passed, 3 deselected in 0.88s
operator: core/ReplaceBinaryOperator_Sub_LShift, occurrence: 18
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -829,7 +829,7 @@
                         )
                         continue
 
-                duration = time.time() - start_time
+                duration = time.time() << start_time
                 github_api_request_duration_seconds.labels(operation="get_user_teams").observe(
                     duration
                 )
................................................................F
=================================== FAILURES ===================================
____________ TestGetUserTeamSlugs.test_get_user_team_slugs_success _____________

self = <tests.test_github_client.TestGetUserTeamSlugs object at 0x7fee95db91d0>

    def test_get_user_team_slugs_success(self):
        """Test successful team membership check."""
    
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            # Mock user
            mock_user = Mock()
            mock_user.login = "alice"
    
            # Mock team that has alice as member
            mock_team = Mock()
            mock_team.has_in_members.return_value = True
    
            # Mock org
            mock_org = Mock()
            mock_org.get_team_by_slug.return_value = mock_team
    
            mock_github = Mock()
            mock_github.get_organization.return_value = mock_org
            mock_github.get_user.return_value = mock_user
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.get_user_team_slugs(
                123456, "acme", "alice", ["release-team", "deploy-team"]
            )
    
>           assert result == ["release-team", "deploy-team"]
E           AssertionError: assert [] == ['release-tea...'deploy-team']
E             
E             Right contains 2 more items, first extra item: 'release-team'
E             
E             Full diff:
E             + []
E             - [
E             -     'release-team',
E             -     'deploy-team',
E             - ]

tests/test_github_client.py:848: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:49:53 [debug    ] User alice is a member of team release-team extra={'org': 'acme', 'username': 'alice', 'team': 'release-team'}
2026-03-27 13:49:53 [debug    ] User alice is a member of team deploy-team extra={'org': 'acme', 'username': 'alice', 'team': 'deploy-team'}
2026-03-27 13:49:53 [warning  ] Failed to check team memberships for alice in acme: unsupported operand type(s) for <<: 'float' and 'float' extra={'org': 'acme', 'username': 'alice', 'installation_id': 123456, 'error': "unsupported operand type(s) for <<: 'float' and 'float'"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetUserTeamSlugs::test_get_user_team_slugs_success - AssertionError: assert [] == ['release-tea...'deploy-team']
  
  Right contains 2 more items, first extra item: 'release-team'
  
  Full diff:
  + []
  - [
  -     'release-team',
  -     'deploy-team',
  - ]
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 64 passed, 3 deselected in 0.77s
operator: core/ReplaceBinaryOperator_Sub_LShift, occurrence: 19
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -849,7 +849,7 @@
                 return member_teams
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() << start_time
                 github_api_request_duration_seconds.labels(operation="get_user_teams").observe(
                     duration
                 )
....................................................................F
=================================== FAILURES ===================================
_______ TestGetUserTeamSlugs.test_get_user_team_slugs_general_exception ________

self = <stampbot.github_client.GitHubAppClient object at 0x7fc0c5902c90>
installation_id = 123456, org_name = 'acme', username = 'alice'
allowed_teams = ['release-team']

    def get_user_team_slugs(
        self,
        installation_id: int,
        org_name: str,
        username: str,
        allowed_teams: list[str],
    ) -> list[str]:
        """Get team slugs the user is a member of from the allowed teams list.
    
        Args:
            installation_id: GitHub App installation ID
            org_name: Organization name
            username: GitHub username to check
            allowed_teams: List of team slugs to check (can be "org/team" or just "team")
    
        Returns:
            List of team slugs the user is a member of
        """
        start_time = time.time()
    
        with create_span(
            "github.get_user_team_slugs",
            {
                "github.org": org_name,
                "github.username": username,
                "github.installation_id": installation_id,
                "github.teams_to_check": len(allowed_teams),
            },
        ) as span:
            member_teams: list[str] = []
    
            try:
                client = self._get_installation_client(installation_id)
>               org = client.get_organization(org_name)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:796: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_organization' id='140465926846320'>
args = ('acme',), kwargs = {}, effect = Exception('Network error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: Network error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestGetUserTeamSlugs object at 0x7fc0c9ed05f0>

    def test_get_user_team_slugs_general_exception(self):
        """Test team membership check with general exception."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_github = Mock()
            mock_github.get_organization.side_effect = Exception("Network error")
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.get_user_team_slugs(123456, "acme", "alice", ["release-team"])
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:1010: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7fc0c5902c90>
installation_id = 123456, org_name = 'acme', username = 'alice'
allowed_teams = ['release-team']

    def get_user_team_slugs(
        self,
        installation_id: int,
        org_name: str,
        username: str,
        allowed_teams: list[str],
    ) -> list[str]:
        """Get team slugs the user is a member of from the allowed teams list.
    
        Args:
            installation_id: GitHub App installation ID
            org_name: Organization name
            username: GitHub username to check
            allowed_teams: List of team slugs to check (can be "org/team" or just "team")
    
        Returns:
            List of team slugs the user is a member of
        """
        start_time = time.time()
    
        with create_span(
            "github.get_user_team_slugs",
            {
                "github.org": org_name,
                "github.username": username,
                "github.installation_id": installation_id,
                "github.teams_to_check": len(allowed_teams),
            },
        ) as span:
            member_teams: list[str] = []
    
            try:
                client = self._get_installation_client(installation_id)
                org = client.get_organization(org_name)
    
                for team_ref in allowed_teams:
                    # Extract team slug (handle both "org/team" and "team" formats)
                    team_slug = team_ref.split("/")[-1] if "/" in team_ref else team_ref
    
                    try:
                        team = org.get_team_by_slug(team_slug)
                        # Check if user is a member
                        user = client.get_user(username)
                        if team.has_in_members(user):  # type: ignore[arg-type]
                            member_teams.append(team_slug)
                            logger.debug(
                                "User %s is a member of team %s",
                                username,
                                team_slug,
                                extra={
                                    "org": org_name,
                                    "username": username,
                                    "team": team_slug,
                                },
                            )
                    except GithubException as e:
                        # Team not found or no access - skip silently
                        logger.debug(
                            "Could not check team %s membership: %s",
                            team_slug,
                            e.data.get("message", str(e)) if hasattr(e, "data") else str(e),
                            extra={
                                "org": org_name,
                                "team": team_slug,
                                "username": username,
                            },
                        )
                        continue
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_user_teams").observe(
                    duration
                )
                github_api_requests_total.labels(operation="get_user_teams", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(
                    span,
                    {
                        "github.member_teams": len(member_teams),
                        "github.teams_checked": len(allowed_teams),
                    },
                )
                set_span_ok(span)
    
                return member_teams
    
            except Exception as e:
>               duration = time.time() << start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for <<: 'float' and 'float'

stampbot/github_client.py:852: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetUserTeamSlugs::test_get_user_team_slugs_general_exception - TypeError: unsupported operand type(s) for <<: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 68 passed, 3 deselected in 0.91s
operator: core/ReplaceBinaryOperator_Sub_BitOr, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -168,7 +168,7 @@
                     retry=retry,
                 )
 
-                duration = time.time() - start_time
+                duration = time.time() | start_time
                 github_api_request_duration_seconds.labels(operation="get_token").observe(duration)
                 github_api_requests_total.labels(operation="get_token", status="success").inc()
 
...............................................F
=================================== FAILURES ===================================
________ TestGetInstallationClient.test_get_installation_client_success ________

self = <tests.test_github_client.TestGetInstallationClient object at 0x7f2b84c54050>

    def test_get_installation_client_success(self):
        """Test successful installation client creation."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client._get_installation_client(123456)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:211: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f2b80c8c830>
installation_id = 123456

    def _get_installation_client(self, installation_id: int) -> Github:
        """Get authenticated GitHub client for an installation.
    
        Args:
            installation_id: GitHub App installation ID.
    
        Returns:
            Authenticated Github client instance with timeout and retry configured.
    
        Raises:
            github.GithubException: If token exchange fails.
        """
        start_time = time.time()
    
        with create_span(
            "github.get_installation_token",
            {"github.installation_id": installation_id},
        ) as span:
            try:
                auth = self.integration.get_access_token(installation_id)
    
                # Configure retry with exponential backoff
                retry = Retry(
                    total=GITHUB_API_RETRY_TOTAL,
                    backoff_factor=GITHUB_API_RETRY_BACKOFF,
                    status_forcelist=[500, 502, 503, 504],
                    allowed_methods=["GET", "POST", "PUT", "DELETE", "PATCH"],
                )
    
                client = Github(
                    auth=Auth.Token(auth.token),
                    timeout=GITHUB_API_TIMEOUT,
                    retry=retry,
                )
    
>               duration = time.time() | start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for |: 'float' and 'float'

stampbot/github_client.py:171: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetInstallationClient::test_get_installation_client_success - TypeError: unsupported operand type(s) for |: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 47 passed, 3 deselected in 0.77s
operator: core/ReplaceBinaryOperator_Sub_BitOr, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -176,7 +176,7 @@
                 return client
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() | start_time
                 github_api_request_duration_seconds.labels(operation="get_token").observe(duration)
                 github_api_requests_total.labels(operation="get_token", status="failure").inc()
                 set_span_error(span, e)
................................................F
=================================== FAILURES ===================================
________ TestGetInstallationClient.test_get_installation_client_failure ________

self = <stampbot.github_client.GitHubAppClient object at 0x7fd447bd1230>
installation_id = 123456

    def _get_installation_client(self, installation_id: int) -> Github:
        """Get authenticated GitHub client for an installation.
    
        Args:
            installation_id: GitHub App installation ID.
    
        Returns:
            Authenticated Github client instance with timeout and retry configured.
    
        Raises:
            github.GithubException: If token exchange fails.
        """
        start_time = time.time()
    
        with create_span(
            "github.get_installation_token",
            {"github.installation_id": installation_id},
        ) as span:
            try:
>               auth = self.integration.get_access_token(installation_id)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:155: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='GithubIntegration().get_access_token' id='140549693947392'>
args = (123456,), kwargs = {}, effect = Exception('Token exchange failed')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: Token exchange failed

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestGetInstallationClient object at 0x7fd44a820190>

    def test_get_installation_client_failure(self):
        """Test installation client creation failure."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_integration.get_access_token.side_effect = Exception("Token exchange failed")
            mock_integration_cls.return_value = mock_integration
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            with pytest.raises(Exception, match="Token exchange failed"):
>               client._get_installation_client(123456)

tests/test_github_client.py:240: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7fd447bd1230>
installation_id = 123456

    def _get_installation_client(self, installation_id: int) -> Github:
        """Get authenticated GitHub client for an installation.
    
        Args:
            installation_id: GitHub App installation ID.
    
        Returns:
            Authenticated Github client instance with timeout and retry configured.
    
        Raises:
            github.GithubException: If token exchange fails.
        """
        start_time = time.time()
    
        with create_span(
            "github.get_installation_token",
            {"github.installation_id": installation_id},
        ) as span:
            try:
                auth = self.integration.get_access_token(installation_id)
    
                # Configure retry with exponential backoff
                retry = Retry(
                    total=GITHUB_API_RETRY_TOTAL,
                    backoff_factor=GITHUB_API_RETRY_BACKOFF,
                    status_forcelist=[500, 502, 503, 504],
                    allowed_methods=["GET", "POST", "PUT", "DELETE", "PATCH"],
                )
    
                client = Github(
                    auth=Auth.Token(auth.token),
                    timeout=GITHUB_API_TIMEOUT,
                    retry=retry,
                )
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_token").observe(duration)
                github_api_requests_total.labels(operation="get_token", status="success").inc()
    
                set_span_ok(span)
                return client
    
            except Exception as e:
>               duration = time.time() | start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for |: 'float' and 'float'

stampbot/github_client.py:179: TypeError

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestGetInstallationClient object at 0x7fd44a820190>

    def test_get_installation_client_failure(self):
        """Test installation client creation failure."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_integration.get_access_token.side_effect = Exception("Token exchange failed")
            mock_integration_cls.return_value = mock_integration
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           with pytest.raises(Exception, match="Token exchange failed"):
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E           AssertionError: Regex pattern did not match.
E             Expected regex: 'Token exchange failed'
E             Actual message: "unsupported operand type(s) for |: 'float' and 'float'"

tests/test_github_client.py:239: AssertionError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetInstallationClient::test_get_installation_client_failure - AssertionError: Regex pattern did not match.
  Expected regex: 'Token exchange failed'
  Actual message: "unsupported operand type(s) for |: 'float' and 'float'"
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 48 passed, 3 deselected in 0.86s
operator: core/ReplaceBinaryOperator_Sub_BitOr, occurrence: 2
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -240,7 +240,7 @@
                     event="APPROVE",
                 )
 
-                duration = time.time() - start_time
+                duration = time.time() | start_time
                 github_api_request_duration_seconds.labels(operation="approve").observe(duration)
                 github_api_requests_total.labels(operation="approve", status="success").inc()
 
...................................................F
=================================== FAILURES ===================================
____________________ TestApprovePR.test_approve_pr_success _____________________

self = <tests.test_github_client.TestApprovePR object at 0x7f1681778550>

    def test_approve_pr_success(self):
        """Test successful PR approval."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_pr = Mock()
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.approve_pr(123456, "owner/repo", 42)
    
>           assert result is True
E           assert False is True

tests/test_github_client.py:313: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:56:29 [error    ] Failed to approve PR #42 in owner/repo: unsupported operand type(s) for |: 'float' and 'float' extra={'repo': 'owner/repo', 'pr_number': 42, 'installation_id': 123456, 'error': "unsupported operand type(s) for |: 'float' and 'float'"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestApprovePR::test_approve_pr_success - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 51 passed, 3 deselected in 0.72s
operator: core/ReplaceBinaryOperator_Sub_BitOr, occurrence: 3
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -260,7 +260,7 @@
                 return True
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() | start_time
                 github_api_request_duration_seconds.labels(operation="approve").observe(duration)
                 github_api_requests_total.labels(operation="approve", status="failure").inc()
 
....................................................F
=================================== FAILURES ===================================
____________________ TestApprovePR.test_approve_pr_failure _____________________

self = <stampbot.github_client.GitHubAppClient object at 0x7fb9d7066c50>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
comment = 'Auto-approved by Stampbot'

    def approve_pr(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        comment: str = "Auto-approved by Stampbot",
    ) -> bool:
        """Approve a pull request.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            comment: Review comment
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.approve_pr",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                # Create approval review
>               pr.create_review(
                    body=comment,
                    event="APPROVE",
                )

stampbot/github_client.py:238: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_pull().create_review' id='140436153612480'>
args = (), kwargs = {'body': 'Auto-approved by Stampbot', 'event': 'APPROVE'}
effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestApprovePR object at 0x7fb9db124690>

    def test_approve_pr_failure(self):
        """Test PR approval failure."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_pr = Mock()
            mock_pr.create_review.side_effect = Exception("API Error")
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.approve_pr(123456, "owner/repo", 42)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:350: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7fb9d7066c50>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
comment = 'Auto-approved by Stampbot'

    def approve_pr(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        comment: str = "Auto-approved by Stampbot",
    ) -> bool:
        """Approve a pull request.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            comment: Review comment
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.approve_pr",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                # Create approval review
                pr.create_review(
                    body=comment,
                    event="APPROVE",
                )
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="approve").observe(duration)
                github_api_requests_total.labels(operation="approve", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.result": "approved"})
                set_span_ok(span)
    
                logger.info(
                    f"Approved PR #{pr_number} in {repo_full_name}",
                    extra={
                        "repo": repo_full_name,
                        "pr_number": pr_number,
                        "installation_id": installation_id,
                    },
                )
                return True
    
            except Exception as e:
>               duration = time.time() | start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for |: 'float' and 'float'

stampbot/github_client.py:263: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestApprovePR::test_approve_pr_failure - TypeError: unsupported operand type(s) for |: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 52 passed, 3 deselected in 0.86s
operator: core/ReplaceBinaryOperator_Sub_BitOr, occurrence: 4
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -317,7 +317,7 @@
                 review = pr.get_review(review_id)
                 review.dismiss(message)
 
-                duration = time.time() - start_time
+                duration = time.time() | start_time
                 github_api_request_duration_seconds.labels(operation="dismiss").observe(duration)
                 github_api_requests_total.labels(operation="dismiss", status="success").inc()
 
.....................................................F
=================================== FAILURES ===================================
______________ TestDismissApproval.test_dismiss_approval_success _______________

self = <tests.test_github_client.TestDismissApproval object at 0x7fac218c47d0>

    def test_dismiss_approval_success(self):
        """Test successful approval dismissal."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_review = Mock()
            mock_pr = Mock()
            mock_pr.get_review.return_value = mock_review
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.dismiss_approval(123456, "owner/repo", 42, 999)
    
>           assert result is True
E           assert False is True

tests/test_github_client.py:396: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:55:48 [error    ] Failed to dismiss approval for PR #42 in owner/repo: unsupported operand type(s) for |: 'float' and 'float' extra={'repo': 'owner/repo', 'pr_number': 42, 'installation_id': 123456, 'error': "unsupported operand type(s) for |: 'float' and 'float'"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestDismissApproval::test_dismiss_approval_success - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 53 passed, 3 deselected in 0.74s
operator: core/ReplaceBinaryOperator_Sub_BitOr, occurrence: 5
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -338,7 +338,7 @@
                 return True
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() | start_time
                 github_api_request_duration_seconds.labels(operation="dismiss").observe(duration)
                 github_api_requests_total.labels(operation="dismiss", status="failure").inc()
 
......................................................F
=================================== FAILURES ===================================
______________ TestDismissApproval.test_dismiss_approval_failure _______________

self = <stampbot.github_client.GitHubAppClient object at 0x7faaf000a850>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
review_id = 999, message = 'Approval dismissed by Stampbot'

    def dismiss_approval(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        review_id: int,
        message: str = "Approval dismissed by Stampbot",
    ) -> bool:
        """Dismiss a pull request approval.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            review_id: Review ID to dismiss
            message: Dismissal message
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.dismiss_approval",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.review_id": review_id,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                # Get the review and dismiss it
                review = pr.get_review(review_id)
>               review.dismiss(message)

stampbot/github_client.py:318: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_pull().get_review().dismiss' id='140372148712320'>
args = ('Approval dismissed by Stampbot',), kwargs = {}
effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestDismissApproval object at 0x7faaf40a0910>

    def test_dismiss_approval_failure(self):
        """Test approval dismissal failure."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_review = Mock()
            mock_review.dismiss.side_effect = Exception("API Error")
            mock_pr = Mock()
            mock_pr.get_review.return_value = mock_review
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.dismiss_approval(123456, "owner/repo", 42, 999)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:435: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7faaf000a850>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
review_id = 999, message = 'Approval dismissed by Stampbot'

    def dismiss_approval(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        review_id: int,
        message: str = "Approval dismissed by Stampbot",
    ) -> bool:
        """Dismiss a pull request approval.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            review_id: Review ID to dismiss
            message: Dismissal message
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.dismiss_approval",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.review_id": review_id,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                # Get the review and dismiss it
                review = pr.get_review(review_id)
                review.dismiss(message)
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="dismiss").observe(duration)
                github_api_requests_total.labels(operation="dismiss", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.result": "dismissed"})
                set_span_ok(span)
    
                logger.info(
                    f"Dismissed approval for PR #{pr_number} in {repo_full_name}",
                    extra={
                        "repo": repo_full_name,
                        "pr_number": pr_number,
                        "installation_id": installation_id,
                        "review_id": review_id,
                    },
                )
                return True
    
            except Exception as e:
>               duration = time.time() | start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for |: 'float' and 'float'

stampbot/github_client.py:341: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestDismissApproval::test_dismiss_approval_failure - TypeError: unsupported operand type(s) for |: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 54 passed, 3 deselected in 0.88s
operator: core/ReplaceBinaryOperator_Sub_BitOr, occurrence: 6
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -393,7 +393,7 @@
 
                 content = repo.get_contents(file_path, ref=ref)  # type: ignore[arg-type]
                 if isinstance(content, list):
-                    duration = time.time() - start_time
+                    duration = time.time() | start_time
                     github_api_request_duration_seconds.labels(operation="get_file").observe(
                         duration
                     )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_BitOr, occurrence: 7
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -402,7 +402,7 @@
                     set_span_ok(span)
                     return None
 
-                duration = time.time() - start_time
+                duration = time.time() | start_time
                 github_api_request_duration_seconds.labels(operation="get_file").observe(duration)
                 github_api_requests_total.labels(operation="get_file", status="success").inc()
 
.......................................................F
=================================== FAILURES ===================================
__________________ TestGetRepoFile.test_get_repo_file_success __________________

self = <tests.test_github_client.TestGetRepoFile object at 0x7fbd531a4a50>

    def test_get_repo_file_success(self):
        """Test successful file retrieval."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_content = Mock()
            mock_content.decoded_content = b"file content"
            mock_repo = Mock()
            mock_repo.get_contents.return_value = mock_content
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.get_repo_file(123456, "owner/repo", "stampbot.toml")
    
>           assert result == "file content"
E           AssertionError: assert None == 'file content'

tests/test_github_client.py:480: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 14:06:34 [debug    ] Could not fetch stampbot.toml from owner/repo: unsupported operand type(s) for |: 'float' and 'float' extra={'repo': 'owner/repo', 'file_path': 'stampbot.toml', 'installation_id': 123456}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetRepoFile::test_get_repo_file_success - AssertionError: assert None == 'file content'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 55 passed, 3 deselected in 0.75s
operator: core/ReplaceBinaryOperator_Sub_BitOr, occurrence: 8
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -414,7 +414,7 @@
                 return content.decoded_content.decode("utf-8")
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() | start_time
                 github_api_request_duration_seconds.labels(operation="get_file").observe(duration)
                 github_api_requests_total.labels(operation="get_file", status="not_found").inc()
 
.........................................................F
=================================== FAILURES ===================================
___________ TestGetRepoFile.test_get_repo_file_returns_none_on_error ___________

self = <stampbot.github_client.GitHubAppClient object at 0x7f21df05ccd0>
installation_id = 123456, repo_full_name = 'owner/repo'
file_path = 'nonexistent.toml', ref = None

    def get_repo_file(
        self,
        installation_id: int,
        repo_full_name: str,
        file_path: str,
        ref: str | None = None,
    ) -> str | None:
        """Get file content from repository.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            file_path: Path to file in repository
            ref: Git reference (branch, tag, commit). Defaults to default branch
    
        Returns:
            File content as string, or None if not found
        """
        start_time = time.time()
    
        with create_span(
            "github.get_file",
            {
                "github.repo": repo_full_name,
                "github.file_path": file_path,
                "github.ref": ref or "default",
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
    
>               content = repo.get_contents(file_path, ref=ref)  # type: ignore[arg-type]
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:394: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_contents' id='139783452657040'>
args = ('nonexistent.toml',), kwargs = {'ref': None}
effect = Exception('File not found')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: File not found

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestGetRepoFile object at 0x7f21e3401940>

    def test_get_repo_file_returns_none_on_error(self):
        """Test that get_repo_file returns None when file not found."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_contents.side_effect = Exception("File not found")
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.get_repo_file(123456, "owner/repo", "nonexistent.toml")
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:551: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f21df05ccd0>
installation_id = 123456, repo_full_name = 'owner/repo'
file_path = 'nonexistent.toml', ref = None

    def get_repo_file(
        self,
        installation_id: int,
        repo_full_name: str,
        file_path: str,
        ref: str | None = None,
    ) -> str | None:
        """Get file content from repository.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            file_path: Path to file in repository
            ref: Git reference (branch, tag, commit). Defaults to default branch
    
        Returns:
            File content as string, or None if not found
        """
        start_time = time.time()
    
        with create_span(
            "github.get_file",
            {
                "github.repo": repo_full_name,
                "github.file_path": file_path,
                "github.ref": ref or "default",
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
    
                content = repo.get_contents(file_path, ref=ref)  # type: ignore[arg-type]
                if isinstance(content, list):
                    duration = time.time() - start_time
                    github_api_request_duration_seconds.labels(operation="get_file").observe(
                        duration
                    )
                    github_api_requests_total.labels(operation="get_file", status="not_found").inc()
                    add_span_attributes(span, {"github.result": "not_found"})
                    set_span_ok(span)
                    return None
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_file").observe(duration)
                github_api_requests_total.labels(operation="get_file", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.result": "found"})
                set_span_ok(span)
    
                return content.decoded_content.decode("utf-8")
    
            except Exception as e:
>               duration = time.time() | start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for |: 'float' and 'float'

stampbot/github_client.py:417: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetRepoFile::test_get_repo_file_returns_none_on_error - TypeError: unsupported operand type(s) for |: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 57 passed, 3 deselected in 0.88s
operator: core/ReplaceBinaryOperator_Sub_BitOr, occurrence: 9
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -473,7 +473,7 @@
                     if review.user.login == bot_user and review.state == "APPROVED":
                         bot_review_ids.append(review.id)
 
-                duration = time.time() - start_time
+                duration = time.time() | start_time
                 github_api_request_duration_seconds.labels(operation="find_reviews").observe(
                     duration
                 )
..........................................................F
=================================== FAILURES ===================================
_______________ TestFindBotReviews.test_find_bot_reviews_success _______________

self = <tests.test_github_client.TestFindBotReviews object at 0x7f52a20e0cd0>

    def test_find_bot_reviews_success(self):
        """Test successful bot review finding."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration.get_app.return_value = Mock(slug="stampbot")
            mock_integration_cls.return_value = mock_integration
    
            # Create mock reviews
            bot_review = Mock()
            bot_review.user.login = "stampbot[bot]"
            bot_review.state = "APPROVED"
            bot_review.id = 123
    
            other_review = Mock()
            other_review.user.login = "other-user"
            other_review.state = "APPROVED"
            other_review.id = 456
    
            mock_pr = Mock()
            mock_pr.get_reviews.return_value = [bot_review, other_review]
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.find_bot_reviews(123456, "owner/repo", 42)
    
>           assert result == [123]
E           assert [] == [123]
E             
E             Right contains one more item: 123
E             
E             Full diff:
E             + []
E             - [
E             -     123,
E             - ]

tests/test_github_client.py:608: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 14:06:46 [error    ] Failed to find bot reviews for PR #42 in owner/repo: unsupported operand type(s) for |: 'float' and 'float' extra={'repo': 'owner/repo', 'pr_number': 42, 'installation_id': 123456, 'error': "unsupported operand type(s) for |: 'float' and 'float'"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestFindBotReviews::test_find_bot_reviews_success - assert [] == [123]
  
  Right contains one more item: 123
  
  Full diff:
  + []
  - [
  -     123,
  - ]
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 58 passed, 3 deselected in 0.76s
operator: core/ReplaceBinaryOperator_Sub_BitOr, occurrence: 10
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -489,7 +489,7 @@
                 return bot_review_ids
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() | start_time
                 github_api_request_duration_seconds.labels(operation="find_reviews").observe(
                     duration
                 )
...........................................................F
=================================== FAILURES ===================================
_______ TestFindBotReviews.test_find_bot_reviews_returns_empty_on_error ________

self = <stampbot.github_client.GitHubAppClient object at 0x7f617156cf30>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42

    def find_bot_reviews(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
    ) -> list[int]:
        """Find all reviews created by this bot on a PR.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
    
        Returns:
            List of review IDs created by the bot
        """
        start_time = time.time()
    
        with create_span(
            "github.find_bot_reviews",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
>               repo = client.get_repo(repo_full_name)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:462: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo' id='140056490557856'>
args = ('owner/repo',), kwargs = {}, effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestFindBotReviews object at 0x7f6175584e10>

    def test_find_bot_reviews_returns_empty_on_error(self):
        """Test that find_bot_reviews returns empty list on error."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_github = Mock()
            mock_github.get_repo.side_effect = Exception("API Error")
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.find_bot_reviews(123456, "owner/repo", 42)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:640: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f617156cf30>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42

    def find_bot_reviews(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
    ) -> list[int]:
        """Find all reviews created by this bot on a PR.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
    
        Returns:
            List of review IDs created by the bot
        """
        start_time = time.time()
    
        with create_span(
            "github.find_bot_reviews",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                # Get bot user via JWT-authenticated integration (installation tokens
                # cannot call GET /user, which is restricted by GitHub Apps integration)
                app_info = self.integration.get_app()
                bot_user = f"{app_info.slug}[bot]"
    
                # Find all reviews by bot that are approvals
                bot_review_ids = []
                for review in pr.get_reviews():
                    if review.user.login == bot_user and review.state == "APPROVED":
                        bot_review_ids.append(review.id)
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="find_reviews").observe(
                    duration
                )
                github_api_requests_total.labels(operation="find_reviews", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(
                    span, {"github.reviews_found": len(bot_review_ids), "github.bot_user": bot_user}
                )
                set_span_ok(span)
    
                return bot_review_ids
    
            except Exception as e:
>               duration = time.time() | start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for |: 'float' and 'float'

stampbot/github_client.py:492: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestFindBotReviews::test_find_bot_reviews_returns_empty_on_error - TypeError: unsupported operand type(s) for |: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 59 passed, 3 deselected in 0.87s
operator: core/ReplaceBinaryOperator_Sub_BitOr, occurrence: 11
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -546,7 +546,7 @@
 
                 pr.create_review(body=message, event="COMMENT")
 
-                duration = time.time() - start_time
+                duration = time.time() | start_time
                 github_api_request_duration_seconds.labels(operation="comment").observe(duration)
                 github_api_requests_total.labels(operation="comment", status="success").inc()
 
........................................................................ [ 34%]
.F
=================================== FAILURES ===================================
_______ TestCreatePrReviewComment.test_create_pr_review_comment_success ________

self = <tests.test_github_client.TestCreatePrReviewComment object at 0x7ff63af716d0>

    def test_create_pr_review_comment_success(self):
        """Test successful review comment creation."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_pr = Mock()
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=1, limit=2))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.create_pr_review_comment(
                123456,
                "owner/repo",
                42,
                "Config error",
            )
    
>           assert result is True
E           assert False is True

tests/test_github_client.py:1213: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:42:31 [warning  ] Failed to post config error review for PR #42 in owner/repo: unsupported operand type(s) for |: 'float' and 'float' extra={'repo': 'owner/repo', 'pr_number': 42, 'installation_id': 123456, 'error': "unsupported operand type(s) for |: 'float' and 'float'"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestCreatePrReviewComment::test_create_pr_review_comment_success - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 73 passed, 3 deselected in 0.81s
operator: core/ReplaceBinaryOperator_Sub_BitOr, occurrence: 12
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -568,7 +568,7 @@
                 return True
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() | start_time
                 github_api_request_duration_seconds.labels(operation="comment").observe(duration)
                 github_api_requests_total.labels(operation="comment", status="failure").inc()
 
........................................................................ [ 34%]
..F
=================================== FAILURES ===================================
_______ TestCreatePrReviewComment.test_create_pr_review_comment_failure ________

self = <stampbot.github_client.GitHubAppClient object at 0x7f608089aed0>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
message = 'Config error'

    def create_pr_review_comment(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        message: str,
    ) -> bool:
        """Create a comment review on a pull request.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            message: Review comment body
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.create_review_comment",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
>               pr = repo.get_pull(pr_number)
                     ^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:545: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_pull' id='140052450052816'>
args = (42,), kwargs = {}, effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestCreatePrReviewComment object at 0x7f6084c2d810>

    def test_create_pr_review_comment_failure(self):
        """Test review comment creation handles errors."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_pull.side_effect = Exception("API Error")
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.create_pr_review_comment(
                123456,
                "owner/repo",
                42,
                "Config error",
            )

tests/test_github_client.py:1248: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f608089aed0>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
message = 'Config error'

    def create_pr_review_comment(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        message: str,
    ) -> bool:
        """Create a comment review on a pull request.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            message: Review comment body
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.create_review_comment",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                pr.create_review(body=message, event="COMMENT")
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="comment").observe(duration)
                github_api_requests_total.labels(operation="comment", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.result": "commented"})
                set_span_ok(span)
    
                logger.info(
                    "Posted config error review on PR #%d in %s",
                    pr_number,
                    repo_full_name,
                    extra={
                        "repo": repo_full_name,
                        "pr_number": pr_number,
                        "installation_id": installation_id,
                    },
                )
                return True
    
            except Exception as e:
>               duration = time.time() | start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for |: 'float' and 'float'

stampbot/github_client.py:571: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestCreatePrReviewComment::test_create_pr_review_comment_failure - TypeError: unsupported operand type(s) for |: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 74 passed, 3 deselected in 0.90s
operator: core/ReplaceBinaryOperator_Sub_BitOr, occurrence: 13
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -619,7 +619,7 @@
                 repo = client.get_repo(repo_full_name)
                 repo.get_label(label_name)
 
-                duration = time.time() - start_time
+                duration = time.time() | start_time
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="success").inc()
 
.....................................................................F
=================================== FAILURES ===================================
__________________ TestRepoHasLabel.test_repo_has_label_true ___________________

self = <tests.test_github_client.TestRepoHasLabel object at 0x7f9187791450>

    def test_repo_has_label_true(self):
        """Test repo_has_label returns True when label exists."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_label.return_value = Mock()
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=1, limit=2))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.repo_has_label(123456, "owner/repo", "autoapprove")
    
>           assert result is True
E           assert None is True

tests/test_github_client.py:1054: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:44:59 [debug    ] Could not verify label autoapprove in owner/repo: unsupported operand type(s) for |: 'float' and 'float' extra={'repo': 'owner/repo', 'label': 'autoapprove', 'installation_id': 123456, 'error': "unsupported operand type(s) for |: 'float' and 'float'"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestRepoHasLabel::test_repo_has_label_true - assert None is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 69 passed, 3 deselected in 0.76s
operator: core/ReplaceBinaryOperator_Sub_BitOr, occurrence: 14
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -630,7 +630,7 @@
                 return True
 
             except GithubException as e:
-                duration = time.time() - start_time
+                duration = time.time() | start_time
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="failure").inc()
 
......................................................................F
=================================== FAILURES ===================================
________________ TestRepoHasLabel.test_repo_has_label_not_found ________________

self = <stampbot.github_client.GitHubAppClient object at 0x7fc68a240cb0>
installation_id = 123456, repo_full_name = 'owner/repo', label_name = 'missing'

    def repo_has_label(
        self,
        installation_id: int,
        repo_full_name: str,
        label_name: str,
    ) -> bool | None:
        """Check whether a repository has a label.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            label_name: Label name to look up
    
        Returns:
            True if label exists, False if not found, None if unknown due to error
        """
        start_time = time.time()
    
        with create_span(
            "github.get_label",
            {
                "github.repo": repo_full_name,
                "github.label": label_name,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
>               repo.get_label(label_name)

stampbot/github_client.py:620: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_label' id='140490697713584'>
args = ('missing',), kwargs = {}
effect = GithubException(404 {"message": "Not Found"})

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               github.GithubException.GithubException: 404 {"message": "Not Found"}

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: GithubException

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestRepoHasLabel object at 0x7fc68e945590>

    def test_repo_has_label_not_found(self):
        """Test repo_has_label returns False when label is missing."""
        from github.GithubException import GithubException
    
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_label.side_effect = GithubException(404, {"message": "Not Found"}, None)
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.repo_has_label(123456, "owner/repo", "missing")
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:1090: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7fc68a240cb0>
installation_id = 123456, repo_full_name = 'owner/repo', label_name = 'missing'

    def repo_has_label(
        self,
        installation_id: int,
        repo_full_name: str,
        label_name: str,
    ) -> bool | None:
        """Check whether a repository has a label.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            label_name: Label name to look up
    
        Returns:
            True if label exists, False if not found, None if unknown due to error
        """
        start_time = time.time()
    
        with create_span(
            "github.get_label",
            {
                "github.repo": repo_full_name,
                "github.label": label_name,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                repo.get_label(label_name)
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                github_api_requests_total.labels(operation="get_label", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.label_found": True})
                set_span_ok(span)
                return True
    
            except GithubException as e:
>               duration = time.time() | start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for |: 'float' and 'float'

stampbot/github_client.py:633: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestRepoHasLabel::test_repo_has_label_not_found - TypeError: unsupported operand type(s) for |: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 70 passed, 3 deselected in 0.90s
operator: core/ReplaceBinaryOperator_Sub_BitOr, occurrence: 15
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -655,7 +655,7 @@
                 return None
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() | start_time
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="failure").inc()
 
........................................................................ [ 34%]
F
=================================== FAILURES ===================================
__________________ TestRepoHasLabel.test_repo_has_label_error __________________

self = <stampbot.github_client.GitHubAppClient object at 0x7f7b5d67f8f0>
installation_id = 123456, repo_full_name = 'owner/repo'
label_name = 'autoapprove'

    def repo_has_label(
        self,
        installation_id: int,
        repo_full_name: str,
        label_name: str,
    ) -> bool | None:
        """Check whether a repository has a label.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            label_name: Label name to look up
    
        Returns:
            True if label exists, False if not found, None if unknown due to error
        """
        start_time = time.time()
    
        with create_span(
            "github.get_label",
            {
                "github.repo": repo_full_name,
                "github.label": label_name,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
>               repo.get_label(label_name)

stampbot/github_client.py:620: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_label' id='140167824692560'>
args = ('autoapprove',), kwargs = {}, effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestRepoHasLabel object at 0x7f7b619da060>

    def test_repo_has_label_error(self):
        """Test repo_has_label returns None on error."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_label.side_effect = Exception("API Error")
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.repo_has_label(123456, "owner/repo", "autoapprove")
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:1164: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f7b5d67f8f0>
installation_id = 123456, repo_full_name = 'owner/repo'
label_name = 'autoapprove'

    def repo_has_label(
        self,
        installation_id: int,
        repo_full_name: str,
        label_name: str,
    ) -> bool | None:
        """Check whether a repository has a label.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            label_name: Label name to look up
    
        Returns:
            True if label exists, False if not found, None if unknown due to error
        """
        start_time = time.time()
    
        with create_span(
            "github.get_label",
            {
                "github.repo": repo_full_name,
                "github.label": label_name,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                repo.get_label(label_name)
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                github_api_requests_total.labels(operation="get_label", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.label_found": True})
                set_span_ok(span)
                return True
    
            except GithubException as e:
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                github_api_requests_total.labels(operation="get_label", status="failure").inc()
    
                if e.status == 404:
                    add_span_attributes(span, {"github.label_found": False})
                    set_span_ok(span)
                    return False
    
                set_span_error(span, e)
                logger.debug(
                    "Could not verify label %s in %s: %s",
                    label_name,
                    repo_full_name,
                    _sanitize_error(e),
                    extra={
                        "repo": repo_full_name,
                        "label": label_name,
                        "installation_id": installation_id,
                        "error": _sanitize_error(e),
                    },
                )
                return None
    
            except Exception as e:
>               duration = time.time() | start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for |: 'float' and 'float'

stampbot/github_client.py:658: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestRepoHasLabel::test_repo_has_label_error - TypeError: unsupported operand type(s) for |: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 72 passed, 3 deselected in 0.91s
operator: core/ReplaceBinaryOperator_Sub_BitOr, occurrence: 16
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -718,7 +718,7 @@
 
                 has_permission = permission_index >= required_index
 
-                duration = time.time() - start_time
+                duration = time.time() | start_time
                 github_api_request_duration_seconds.labels(operation="get_permission").observe(
                     duration
                 )
............................................................F
=================================== FAILURES ===================================
_____________ TestUserHasPermission.test_user_has_permission_true ______________

self = <tests.test_github_client.TestUserHasPermission object at 0x7fbefb308f50>

    def test_user_has_permission_true(self):
        """Test required permission satisfied returns True."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_collaborator_permission.return_value = "write"
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=1, limit=2))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.user_has_permission(123456, "owner/repo", "alice", "write")
    
>           assert result is True
E           assert False is True

tests/test_github_client.py:683: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 14:11:38 [warning  ] Failed to check collaborator permission for alice in owner/repo: unsupported operand type(s) for |: 'float' and 'float' extra={'repo': 'owner/repo', 'username': 'alice', 'installation_id': 123456, 'error': "unsupported operand type(s) for |: 'float' and 'float'"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestUserHasPermission::test_user_has_permission_true - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 60 passed, 3 deselected in 0.77s
operator: core/ReplaceBinaryOperator_Sub_BitOr, occurrence: 17
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -738,7 +738,7 @@
                 return has_permission
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() | start_time
                 github_api_request_duration_seconds.labels(operation="get_permission").observe(
                     duration
                 )
..............................................................F
=================================== FAILURES ===================================
_____________ TestUserHasPermission.test_user_has_permission_error _____________

self = <stampbot.github_client.GitHubAppClient object at 0x7f2b85c40410>
installation_id = 123456, repo_full_name = 'owner/repo', username = 'carol'
required_permission = 'maintain'

    def user_has_permission(
        self,
        installation_id: int,
        repo_full_name: str,
        username: str,
        required_permission: str,
    ) -> bool:
        """Check whether a user has required access to a repository.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            username: GitHub username to check
            required_permission: Minimum required repository permission
    
        Returns:
            True if user meets or exceeds required permission, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.get_collaborator_permission",
            {
                "github.repo": repo_full_name,
                "github.username": username,
                "github.required_permission": required_permission,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
    
>               permission = repo.get_collaborator_permission(username)
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:710: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_collaborator_permission' id='139824904489680'>
args = ('carol',), kwargs = {}, effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestUserHasPermission object at 0x7f2b8a10da70>

    def test_user_has_permission_error(self):
        """Test permission check returns False on error."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_collaborator_permission.side_effect = Exception("API Error")
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.user_has_permission(123456, "owner/repo", "carol", "maintain")
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:754: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f2b85c40410>
installation_id = 123456, repo_full_name = 'owner/repo', username = 'carol'
required_permission = 'maintain'

    def user_has_permission(
        self,
        installation_id: int,
        repo_full_name: str,
        username: str,
        required_permission: str,
    ) -> bool:
        """Check whether a user has required access to a repository.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            username: GitHub username to check
            required_permission: Minimum required repository permission
    
        Returns:
            True if user meets or exceeds required permission, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.get_collaborator_permission",
            {
                "github.repo": repo_full_name,
                "github.username": username,
                "github.required_permission": required_permission,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
    
                permission = repo.get_collaborator_permission(username)
                permission_order = ["none", "read", "triage", "write", "maintain", "admin"]
                try:
                    permission_index = permission_order.index(permission)
                    required_index = permission_order.index(required_permission)
                except ValueError:
                    permission_index = -1
                    required_index = 99
    
                has_permission = permission_index >= required_index
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_permission").observe(
                    duration
                )
                github_api_requests_total.labels(operation="get_permission", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(
                    span,
                    {
                        "github.permission": permission,
                        "github.has_permission": has_permission,
                    },
                )
                set_span_ok(span)
    
                return has_permission
    
            except Exception as e:
>               duration = time.time() | start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for |: 'float' and 'float'

stampbot/github_client.py:741: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestUserHasPermission::test_user_has_permission_error - TypeError: unsupported operand type(s) for |: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 62 passed, 3 deselected in 0.88s
operator: core/ReplaceBinaryOperator_Sub_BitOr, occurrence: 18
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -829,7 +829,7 @@
                         )
                         continue
 
-                duration = time.time() - start_time
+                duration = time.time() | start_time
                 github_api_request_duration_seconds.labels(operation="get_user_teams").observe(
                     duration
                 )
................................................................F
=================================== FAILURES ===================================
____________ TestGetUserTeamSlugs.test_get_user_team_slugs_success _____________

self = <tests.test_github_client.TestGetUserTeamSlugs object at 0x7f06206711d0>

    def test_get_user_team_slugs_success(self):
        """Test successful team membership check."""
    
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            # Mock user
            mock_user = Mock()
            mock_user.login = "alice"
    
            # Mock team that has alice as member
            mock_team = Mock()
            mock_team.has_in_members.return_value = True
    
            # Mock org
            mock_org = Mock()
            mock_org.get_team_by_slug.return_value = mock_team
    
            mock_github = Mock()
            mock_github.get_organization.return_value = mock_org
            mock_github.get_user.return_value = mock_user
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.get_user_team_slugs(
                123456, "acme", "alice", ["release-team", "deploy-team"]
            )
    
>           assert result == ["release-team", "deploy-team"]
E           AssertionError: assert [] == ['release-tea...'deploy-team']
E             
E             Right contains 2 more items, first extra item: 'release-team'
E             
E             Full diff:
E             + []
E             - [
E             -     'release-team',
E             -     'deploy-team',
E             - ]

tests/test_github_client.py:848: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:42:53 [debug    ] User alice is a member of team release-team extra={'org': 'acme', 'username': 'alice', 'team': 'release-team'}
2026-03-27 13:42:53 [debug    ] User alice is a member of team deploy-team extra={'org': 'acme', 'username': 'alice', 'team': 'deploy-team'}
2026-03-27 13:42:53 [warning  ] Failed to check team memberships for alice in acme: unsupported operand type(s) for |: 'float' and 'float' extra={'org': 'acme', 'username': 'alice', 'installation_id': 123456, 'error': "unsupported operand type(s) for |: 'float' and 'float'"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetUserTeamSlugs::test_get_user_team_slugs_success - AssertionError: assert [] == ['release-tea...'deploy-team']
  
  Right contains 2 more items, first extra item: 'release-team'
  
  Full diff:
  + []
  - [
  -     'release-team',
  -     'deploy-team',
  - ]
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 64 passed, 3 deselected in 0.77s
operator: core/ReplaceBinaryOperator_Sub_BitOr, occurrence: 19
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -849,7 +849,7 @@
                 return member_teams
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() | start_time
                 github_api_request_duration_seconds.labels(operation="get_user_teams").observe(
                     duration
                 )
....................................................................F
=================================== FAILURES ===================================
_______ TestGetUserTeamSlugs.test_get_user_team_slugs_general_exception ________

self = <stampbot.github_client.GitHubAppClient object at 0x7f4b93e11d90>
installation_id = 123456, org_name = 'acme', username = 'alice'
allowed_teams = ['release-team']

    def get_user_team_slugs(
        self,
        installation_id: int,
        org_name: str,
        username: str,
        allowed_teams: list[str],
    ) -> list[str]:
        """Get team slugs the user is a member of from the allowed teams list.
    
        Args:
            installation_id: GitHub App installation ID
            org_name: Organization name
            username: GitHub username to check
            allowed_teams: List of team slugs to check (can be "org/team" or just "team")
    
        Returns:
            List of team slugs the user is a member of
        """
        start_time = time.time()
    
        with create_span(
            "github.get_user_team_slugs",
            {
                "github.org": org_name,
                "github.username": username,
                "github.installation_id": installation_id,
                "github.teams_to_check": len(allowed_teams),
            },
        ) as span:
            member_teams: list[str] = []
    
            try:
                client = self._get_installation_client(installation_id)
>               org = client.get_organization(org_name)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:796: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_organization' id='139962582136688'>
args = ('acme',), kwargs = {}, effect = Exception('Network error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: Network error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestGetUserTeamSlugs object at 0x7f4b9c3505f0>

    def test_get_user_team_slugs_general_exception(self):
        """Test team membership check with general exception."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_github = Mock()
            mock_github.get_organization.side_effect = Exception("Network error")
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.get_user_team_slugs(123456, "acme", "alice", ["release-team"])
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:1010: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f4b93e11d90>
installation_id = 123456, org_name = 'acme', username = 'alice'
allowed_teams = ['release-team']

    def get_user_team_slugs(
        self,
        installation_id: int,
        org_name: str,
        username: str,
        allowed_teams: list[str],
    ) -> list[str]:
        """Get team slugs the user is a member of from the allowed teams list.
    
        Args:
            installation_id: GitHub App installation ID
            org_name: Organization name
            username: GitHub username to check
            allowed_teams: List of team slugs to check (can be "org/team" or just "team")
    
        Returns:
            List of team slugs the user is a member of
        """
        start_time = time.time()
    
        with create_span(
            "github.get_user_team_slugs",
            {
                "github.org": org_name,
                "github.username": username,
                "github.installation_id": installation_id,
                "github.teams_to_check": len(allowed_teams),
            },
        ) as span:
            member_teams: list[str] = []
    
            try:
                client = self._get_installation_client(installation_id)
                org = client.get_organization(org_name)
    
                for team_ref in allowed_teams:
                    # Extract team slug (handle both "org/team" and "team" formats)
                    team_slug = team_ref.split("/")[-1] if "/" in team_ref else team_ref
    
                    try:
                        team = org.get_team_by_slug(team_slug)
                        # Check if user is a member
                        user = client.get_user(username)
                        if team.has_in_members(user):  # type: ignore[arg-type]
                            member_teams.append(team_slug)
                            logger.debug(
                                "User %s is a member of team %s",
                                username,
                                team_slug,
                                extra={
                                    "org": org_name,
                                    "username": username,
                                    "team": team_slug,
                                },
                            )
                    except GithubException as e:
                        # Team not found or no access - skip silently
                        logger.debug(
                            "Could not check team %s membership: %s",
                            team_slug,
                            e.data.get("message", str(e)) if hasattr(e, "data") else str(e),
                            extra={
                                "org": org_name,
                                "team": team_slug,
                                "username": username,
                            },
                        )
                        continue
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_user_teams").observe(
                    duration
                )
                github_api_requests_total.labels(operation="get_user_teams", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(
                    span,
                    {
                        "github.member_teams": len(member_teams),
                        "github.teams_checked": len(allowed_teams),
                    },
                )
                set_span_ok(span)
    
                return member_teams
    
            except Exception as e:
>               duration = time.time() | start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for |: 'float' and 'float'

stampbot/github_client.py:852: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetUserTeamSlugs::test_get_user_team_slugs_general_exception - TypeError: unsupported operand type(s) for |: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 68 passed, 3 deselected in 0.90s
operator: core/ReplaceBinaryOperator_Sub_BitAnd, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -168,7 +168,7 @@
                     retry=retry,
                 )
 
-                duration = time.time() - start_time
+                duration = time.time() & start_time
                 github_api_request_duration_seconds.labels(operation="get_token").observe(duration)
                 github_api_requests_total.labels(operation="get_token", status="success").inc()
 
...............................................F
=================================== FAILURES ===================================
________ TestGetInstallationClient.test_get_installation_client_success ________

self = <tests.test_github_client.TestGetInstallationClient object at 0x7efef1f4c050>

    def test_get_installation_client_success(self):
        """Test successful installation client creation."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client._get_installation_client(123456)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:211: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7efeee098830>
installation_id = 123456

    def _get_installation_client(self, installation_id: int) -> Github:
        """Get authenticated GitHub client for an installation.
    
        Args:
            installation_id: GitHub App installation ID.
    
        Returns:
            Authenticated Github client instance with timeout and retry configured.
    
        Raises:
            github.GithubException: If token exchange fails.
        """
        start_time = time.time()
    
        with create_span(
            "github.get_installation_token",
            {"github.installation_id": installation_id},
        ) as span:
            try:
                auth = self.integration.get_access_token(installation_id)
    
                # Configure retry with exponential backoff
                retry = Retry(
                    total=GITHUB_API_RETRY_TOTAL,
                    backoff_factor=GITHUB_API_RETRY_BACKOFF,
                    status_forcelist=[500, 502, 503, 504],
                    allowed_methods=["GET", "POST", "PUT", "DELETE", "PATCH"],
                )
    
                client = Github(
                    auth=Auth.Token(auth.token),
                    timeout=GITHUB_API_TIMEOUT,
                    retry=retry,
                )
    
>               duration = time.time() & start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for &: 'float' and 'float'

stampbot/github_client.py:171: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetInstallationClient::test_get_installation_client_success - TypeError: unsupported operand type(s) for &: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 47 passed, 3 deselected in 0.78s
operator: core/ReplaceBinaryOperator_Sub_BitAnd, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -176,7 +176,7 @@
                 return client
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() & start_time
                 github_api_request_duration_seconds.labels(operation="get_token").observe(duration)
                 github_api_requests_total.labels(operation="get_token", status="failure").inc()
                 set_span_error(span, e)
................................................F
=================================== FAILURES ===================================
________ TestGetInstallationClient.test_get_installation_client_failure ________

self = <stampbot.github_client.GitHubAppClient object at 0x7f4ababd1230>
installation_id = 123456

    def _get_installation_client(self, installation_id: int) -> Github:
        """Get authenticated GitHub client for an installation.
    
        Args:
            installation_id: GitHub App installation ID.
    
        Returns:
            Authenticated Github client instance with timeout and retry configured.
    
        Raises:
            github.GithubException: If token exchange fails.
        """
        start_time = time.time()
    
        with create_span(
            "github.get_installation_token",
            {"github.installation_id": installation_id},
        ) as span:
            try:
>               auth = self.integration.get_access_token(installation_id)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:155: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='GithubIntegration().get_access_token' id='139958917840384'>
args = (123456,), kwargs = {}, effect = Exception('Token exchange failed')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: Token exchange failed

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestGetInstallationClient object at 0x7f4abdc4c190>

    def test_get_installation_client_failure(self):
        """Test installation client creation failure."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_integration.get_access_token.side_effect = Exception("Token exchange failed")
            mock_integration_cls.return_value = mock_integration
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            with pytest.raises(Exception, match="Token exchange failed"):
>               client._get_installation_client(123456)

tests/test_github_client.py:240: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f4ababd1230>
installation_id = 123456

    def _get_installation_client(self, installation_id: int) -> Github:
        """Get authenticated GitHub client for an installation.
    
        Args:
            installation_id: GitHub App installation ID.
    
        Returns:
            Authenticated Github client instance with timeout and retry configured.
    
        Raises:
            github.GithubException: If token exchange fails.
        """
        start_time = time.time()
    
        with create_span(
            "github.get_installation_token",
            {"github.installation_id": installation_id},
        ) as span:
            try:
                auth = self.integration.get_access_token(installation_id)
    
                # Configure retry with exponential backoff
                retry = Retry(
                    total=GITHUB_API_RETRY_TOTAL,
                    backoff_factor=GITHUB_API_RETRY_BACKOFF,
                    status_forcelist=[500, 502, 503, 504],
                    allowed_methods=["GET", "POST", "PUT", "DELETE", "PATCH"],
                )
    
                client = Github(
                    auth=Auth.Token(auth.token),
                    timeout=GITHUB_API_TIMEOUT,
                    retry=retry,
                )
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_token").observe(duration)
                github_api_requests_total.labels(operation="get_token", status="success").inc()
    
                set_span_ok(span)
                return client
    
            except Exception as e:
>               duration = time.time() & start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for &: 'float' and 'float'

stampbot/github_client.py:179: TypeError

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestGetInstallationClient object at 0x7f4abdc4c190>

    def test_get_installation_client_failure(self):
        """Test installation client creation failure."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_integration.get_access_token.side_effect = Exception("Token exchange failed")
            mock_integration_cls.return_value = mock_integration
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           with pytest.raises(Exception, match="Token exchange failed"):
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E           AssertionError: Regex pattern did not match.
E             Expected regex: 'Token exchange failed'
E             Actual message: "unsupported operand type(s) for &: 'float' and 'float'"

tests/test_github_client.py:239: AssertionError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetInstallationClient::test_get_installation_client_failure - AssertionError: Regex pattern did not match.
  Expected regex: 'Token exchange failed'
  Actual message: "unsupported operand type(s) for &: 'float' and 'float'"
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 48 passed, 3 deselected in 0.86s
operator: core/ReplaceBinaryOperator_Sub_BitAnd, occurrence: 2
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -240,7 +240,7 @@
                     event="APPROVE",
                 )
 
-                duration = time.time() - start_time
+                duration = time.time() & start_time
                 github_api_request_duration_seconds.labels(operation="approve").observe(duration)
                 github_api_requests_total.labels(operation="approve", status="success").inc()
 
...................................................F
=================================== FAILURES ===================================
____________________ TestApprovePR.test_approve_pr_success _____________________

self = <tests.test_github_client.TestApprovePR object at 0x7f1c71868550>

    def test_approve_pr_success(self):
        """Test successful PR approval."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_pr = Mock()
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.approve_pr(123456, "owner/repo", 42)
    
>           assert result is True
E           assert False is True

tests/test_github_client.py:313: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:36:32 [error    ] Failed to approve PR #42 in owner/repo: unsupported operand type(s) for &: 'float' and 'float' extra={'repo': 'owner/repo', 'pr_number': 42, 'installation_id': 123456, 'error': "unsupported operand type(s) for &: 'float' and 'float'"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestApprovePR::test_approve_pr_success - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 51 passed, 3 deselected in 0.74s
operator: core/ReplaceBinaryOperator_Sub_BitAnd, occurrence: 3
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -260,7 +260,7 @@
                 return True
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() & start_time
                 github_api_request_duration_seconds.labels(operation="approve").observe(duration)
                 github_api_requests_total.labels(operation="approve", status="failure").inc()
 
....................................................F
=================================== FAILURES ===================================
____________________ TestApprovePR.test_approve_pr_failure _____________________

self = <stampbot.github_client.GitHubAppClient object at 0x7f4ebf266c50>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
comment = 'Auto-approved by Stampbot'

    def approve_pr(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        comment: str = "Auto-approved by Stampbot",
    ) -> bool:
        """Approve a pull request.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            comment: Review comment
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.approve_pr",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                # Create approval review
>               pr.create_review(
                    body=comment,
                    event="APPROVE",
                )

stampbot/github_client.py:238: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_pull().create_review' id='139976191555776'>
args = (), kwargs = {'body': 'Auto-approved by Stampbot', 'event': 'APPROVE'}
effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestApprovePR object at 0x7f4ec35fc690>

    def test_approve_pr_failure(self):
        """Test PR approval failure."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_pr = Mock()
            mock_pr.create_review.side_effect = Exception("API Error")
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.approve_pr(123456, "owner/repo", 42)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:350: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f4ebf266c50>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
comment = 'Auto-approved by Stampbot'

    def approve_pr(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        comment: str = "Auto-approved by Stampbot",
    ) -> bool:
        """Approve a pull request.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            comment: Review comment
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.approve_pr",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                # Create approval review
                pr.create_review(
                    body=comment,
                    event="APPROVE",
                )
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="approve").observe(duration)
                github_api_requests_total.labels(operation="approve", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.result": "approved"})
                set_span_ok(span)
    
                logger.info(
                    f"Approved PR #{pr_number} in {repo_full_name}",
                    extra={
                        "repo": repo_full_name,
                        "pr_number": pr_number,
                        "installation_id": installation_id,
                    },
                )
                return True
    
            except Exception as e:
>               duration = time.time() & start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for &: 'float' and 'float'

stampbot/github_client.py:263: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestApprovePR::test_approve_pr_failure - TypeError: unsupported operand type(s) for &: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 52 passed, 3 deselected in 0.87s
operator: core/ReplaceBinaryOperator_Sub_BitAnd, occurrence: 4
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -317,7 +317,7 @@
                 review = pr.get_review(review_id)
                 review.dismiss(message)
 
-                duration = time.time() - start_time
+                duration = time.time() & start_time
                 github_api_request_duration_seconds.labels(operation="dismiss").observe(duration)
                 github_api_requests_total.labels(operation="dismiss", status="success").inc()
 
.....................................................F
=================================== FAILURES ===================================
______________ TestDismissApproval.test_dismiss_approval_success _______________

self = <tests.test_github_client.TestDismissApproval object at 0x7f1e0d5387d0>

    def test_dismiss_approval_success(self):
        """Test successful approval dismissal."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_review = Mock()
            mock_pr = Mock()
            mock_pr.get_review.return_value = mock_review
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.dismiss_approval(123456, "owner/repo", 42, 999)
    
>           assert result is True
E           assert False is True

tests/test_github_client.py:396: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:39:23 [error    ] Failed to dismiss approval for PR #42 in owner/repo: unsupported operand type(s) for &: 'float' and 'float' extra={'repo': 'owner/repo', 'pr_number': 42, 'installation_id': 123456, 'error': "unsupported operand type(s) for &: 'float' and 'float'"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestDismissApproval::test_dismiss_approval_success - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 53 passed, 3 deselected in 0.74s
operator: core/ReplaceBinaryOperator_Sub_BitAnd, occurrence: 5
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -338,7 +338,7 @@
                 return True
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() & start_time
                 github_api_request_duration_seconds.labels(operation="dismiss").observe(duration)
                 github_api_requests_total.labels(operation="dismiss", status="failure").inc()
 
......................................................F
=================================== FAILURES ===================================
______________ TestDismissApproval.test_dismiss_approval_failure _______________

self = <stampbot.github_client.GitHubAppClient object at 0x7fe6e5c0e850>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
review_id = 999, message = 'Approval dismissed by Stampbot'

    def dismiss_approval(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        review_id: int,
        message: str = "Approval dismissed by Stampbot",
    ) -> bool:
        """Dismiss a pull request approval.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            review_id: Review ID to dismiss
            message: Dismissal message
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.dismiss_approval",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.review_id": review_id,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                # Get the review and dismiss it
                review = pr.get_review(review_id)
>               review.dismiss(message)

stampbot/github_client.py:318: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_pull().get_review().dismiss' id='140629674767232'>
args = ('Approval dismissed by Stampbot',), kwargs = {}
effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestDismissApproval object at 0x7fe6e9cb4910>

    def test_dismiss_approval_failure(self):
        """Test approval dismissal failure."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_review = Mock()
            mock_review.dismiss.side_effect = Exception("API Error")
            mock_pr = Mock()
            mock_pr.get_review.return_value = mock_review
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.dismiss_approval(123456, "owner/repo", 42, 999)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:435: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7fe6e5c0e850>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
review_id = 999, message = 'Approval dismissed by Stampbot'

    def dismiss_approval(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        review_id: int,
        message: str = "Approval dismissed by Stampbot",
    ) -> bool:
        """Dismiss a pull request approval.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            review_id: Review ID to dismiss
            message: Dismissal message
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.dismiss_approval",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.review_id": review_id,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                # Get the review and dismiss it
                review = pr.get_review(review_id)
                review.dismiss(message)
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="dismiss").observe(duration)
                github_api_requests_total.labels(operation="dismiss", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.result": "dismissed"})
                set_span_ok(span)
    
                logger.info(
                    f"Dismissed approval for PR #{pr_number} in {repo_full_name}",
                    extra={
                        "repo": repo_full_name,
                        "pr_number": pr_number,
                        "installation_id": installation_id,
                        "review_id": review_id,
                    },
                )
                return True
    
            except Exception as e:
>               duration = time.time() & start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for &: 'float' and 'float'

stampbot/github_client.py:341: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestDismissApproval::test_dismiss_approval_failure - TypeError: unsupported operand type(s) for &: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 54 passed, 3 deselected in 0.86s
operator: core/ReplaceBinaryOperator_Sub_BitAnd, occurrence: 6
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -393,7 +393,7 @@
 
                 content = repo.get_contents(file_path, ref=ref)  # type: ignore[arg-type]
                 if isinstance(content, list):
-                    duration = time.time() - start_time
+                    duration = time.time() & start_time
                     github_api_request_duration_seconds.labels(operation="get_file").observe(
                         duration
                     )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_BitAnd, occurrence: 7
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -402,7 +402,7 @@
                     set_span_ok(span)
                     return None
 
-                duration = time.time() - start_time
+                duration = time.time() & start_time
                 github_api_request_duration_seconds.labels(operation="get_file").observe(duration)
                 github_api_requests_total.labels(operation="get_file", status="success").inc()
 
.......................................................F
=================================== FAILURES ===================================
__________________ TestGetRepoFile.test_get_repo_file_success __________________

self = <tests.test_github_client.TestGetRepoFile object at 0x7f132c874a50>

    def test_get_repo_file_success(self):
        """Test successful file retrieval."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_content = Mock()
            mock_content.decoded_content = b"file content"
            mock_repo = Mock()
            mock_repo.get_contents.return_value = mock_content
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.get_repo_file(123456, "owner/repo", "stampbot.toml")
    
>           assert result == "file content"
E           AssertionError: assert None == 'file content'

tests/test_github_client.py:480: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:58:01 [debug    ] Could not fetch stampbot.toml from owner/repo: unsupported operand type(s) for &: 'float' and 'float' extra={'repo': 'owner/repo', 'file_path': 'stampbot.toml', 'installation_id': 123456}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetRepoFile::test_get_repo_file_success - AssertionError: assert None == 'file content'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 55 passed, 3 deselected in 0.75s
operator: core/ReplaceBinaryOperator_Sub_BitAnd, occurrence: 8
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -414,7 +414,7 @@
                 return content.decoded_content.decode("utf-8")
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() & start_time
                 github_api_request_duration_seconds.labels(operation="get_file").observe(duration)
                 github_api_requests_total.labels(operation="get_file", status="not_found").inc()
 
.........................................................F
=================================== FAILURES ===================================
___________ TestGetRepoFile.test_get_repo_file_returns_none_on_error ___________

self = <stampbot.github_client.GitHubAppClient object at 0x7fea25148cd0>
installation_id = 123456, repo_full_name = 'owner/repo'
file_path = 'nonexistent.toml', ref = None

    def get_repo_file(
        self,
        installation_id: int,
        repo_full_name: str,
        file_path: str,
        ref: str | None = None,
    ) -> str | None:
        """Get file content from repository.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            file_path: Path to file in repository
            ref: Git reference (branch, tag, commit). Defaults to default branch
    
        Returns:
            File content as string, or None if not found
        """
        start_time = time.time()
    
        with create_span(
            "github.get_file",
            {
                "github.repo": repo_full_name,
                "github.file_path": file_path,
                "github.ref": ref or "default",
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
    
>               content = repo.get_contents(file_path, ref=ref)  # type: ignore[arg-type]
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:394: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_contents' id='140643621488016'>
args = ('nonexistent.toml',), kwargs = {'ref': None}
effect = Exception('File not found')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: File not found

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestGetRepoFile object at 0x7fea29509940>

    def test_get_repo_file_returns_none_on_error(self):
        """Test that get_repo_file returns None when file not found."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_contents.side_effect = Exception("File not found")
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.get_repo_file(123456, "owner/repo", "nonexistent.toml")
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:551: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7fea25148cd0>
installation_id = 123456, repo_full_name = 'owner/repo'
file_path = 'nonexistent.toml', ref = None

    def get_repo_file(
        self,
        installation_id: int,
        repo_full_name: str,
        file_path: str,
        ref: str | None = None,
    ) -> str | None:
        """Get file content from repository.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            file_path: Path to file in repository
            ref: Git reference (branch, tag, commit). Defaults to default branch
    
        Returns:
            File content as string, or None if not found
        """
        start_time = time.time()
    
        with create_span(
            "github.get_file",
            {
                "github.repo": repo_full_name,
                "github.file_path": file_path,
                "github.ref": ref or "default",
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
    
                content = repo.get_contents(file_path, ref=ref)  # type: ignore[arg-type]
                if isinstance(content, list):
                    duration = time.time() - start_time
                    github_api_request_duration_seconds.labels(operation="get_file").observe(
                        duration
                    )
                    github_api_requests_total.labels(operation="get_file", status="not_found").inc()
                    add_span_attributes(span, {"github.result": "not_found"})
                    set_span_ok(span)
                    return None
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_file").observe(duration)
                github_api_requests_total.labels(operation="get_file", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.result": "found"})
                set_span_ok(span)
    
                return content.decoded_content.decode("utf-8")
    
            except Exception as e:
>               duration = time.time() & start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for &: 'float' and 'float'

stampbot/github_client.py:417: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetRepoFile::test_get_repo_file_returns_none_on_error - TypeError: unsupported operand type(s) for &: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 57 passed, 3 deselected in 0.87s
operator: core/ReplaceBinaryOperator_Sub_BitAnd, occurrence: 9
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -473,7 +473,7 @@
                     if review.user.login == bot_user and review.state == "APPROVED":
                         bot_review_ids.append(review.id)
 
-                duration = time.time() - start_time
+                duration = time.time() & start_time
                 github_api_request_duration_seconds.labels(operation="find_reviews").observe(
                     duration
                 )
..........................................................F
=================================== FAILURES ===================================
_______________ TestFindBotReviews.test_find_bot_reviews_success _______________

self = <tests.test_github_client.TestFindBotReviews object at 0x7fb184ab4cd0>

    def test_find_bot_reviews_success(self):
        """Test successful bot review finding."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration.get_app.return_value = Mock(slug="stampbot")
            mock_integration_cls.return_value = mock_integration
    
            # Create mock reviews
            bot_review = Mock()
            bot_review.user.login = "stampbot[bot]"
            bot_review.state = "APPROVED"
            bot_review.id = 123
    
            other_review = Mock()
            other_review.user.login = "other-user"
            other_review.state = "APPROVED"
            other_review.id = 456
    
            mock_pr = Mock()
            mock_pr.get_reviews.return_value = [bot_review, other_review]
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.find_bot_reviews(123456, "owner/repo", 42)
    
>           assert result == [123]
E           assert [] == [123]
E             
E             Right contains one more item: 123
E             
E             Full diff:
E             + []
E             - [
E             -     123,
E             - ]

tests/test_github_client.py:608: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:48:36 [error    ] Failed to find bot reviews for PR #42 in owner/repo: unsupported operand type(s) for &: 'float' and 'float' extra={'repo': 'owner/repo', 'pr_number': 42, 'installation_id': 123456, 'error': "unsupported operand type(s) for &: 'float' and 'float'"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestFindBotReviews::test_find_bot_reviews_success - assert [] == [123]
  
  Right contains one more item: 123
  
  Full diff:
  + []
  - [
  -     123,
  - ]
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 58 passed, 3 deselected in 0.75s
operator: core/ReplaceBinaryOperator_Sub_BitAnd, occurrence: 10
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -489,7 +489,7 @@
                 return bot_review_ids
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() & start_time
                 github_api_request_duration_seconds.labels(operation="find_reviews").observe(
                     duration
                 )
...........................................................F
=================================== FAILURES ===================================
_______ TestFindBotReviews.test_find_bot_reviews_returns_empty_on_error ________

self = <stampbot.github_client.GitHubAppClient object at 0x7fe54dd84f30>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42

    def find_bot_reviews(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
    ) -> list[int]:
        """Find all reviews created by this bot on a PR.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
    
        Returns:
            List of review IDs created by the bot
        """
        start_time = time.time()
    
        with create_span(
            "github.find_bot_reviews",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
>               repo = client.get_repo(repo_full_name)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:462: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo' id='140622830731680'>
args = ('owner/repo',), kwargs = {}, effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestFindBotReviews object at 0x7fe5521e0e10>

    def test_find_bot_reviews_returns_empty_on_error(self):
        """Test that find_bot_reviews returns empty list on error."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_github = Mock()
            mock_github.get_repo.side_effect = Exception("API Error")
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.find_bot_reviews(123456, "owner/repo", 42)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:640: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7fe54dd84f30>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42

    def find_bot_reviews(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
    ) -> list[int]:
        """Find all reviews created by this bot on a PR.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
    
        Returns:
            List of review IDs created by the bot
        """
        start_time = time.time()
    
        with create_span(
            "github.find_bot_reviews",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                # Get bot user via JWT-authenticated integration (installation tokens
                # cannot call GET /user, which is restricted by GitHub Apps integration)
                app_info = self.integration.get_app()
                bot_user = f"{app_info.slug}[bot]"
    
                # Find all reviews by bot that are approvals
                bot_review_ids = []
                for review in pr.get_reviews():
                    if review.user.login == bot_user and review.state == "APPROVED":
                        bot_review_ids.append(review.id)
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="find_reviews").observe(
                    duration
                )
                github_api_requests_total.labels(operation="find_reviews", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(
                    span, {"github.reviews_found": len(bot_review_ids), "github.bot_user": bot_user}
                )
                set_span_ok(span)
    
                return bot_review_ids
    
            except Exception as e:
>               duration = time.time() & start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for &: 'float' and 'float'

stampbot/github_client.py:492: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestFindBotReviews::test_find_bot_reviews_returns_empty_on_error - TypeError: unsupported operand type(s) for &: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 59 passed, 3 deselected in 0.86s
operator: core/ReplaceBinaryOperator_Sub_BitAnd, occurrence: 11
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -546,7 +546,7 @@
 
                 pr.create_review(body=message, event="COMMENT")
 
-                duration = time.time() - start_time
+                duration = time.time() & start_time
                 github_api_request_duration_seconds.labels(operation="comment").observe(duration)
                 github_api_requests_total.labels(operation="comment", status="success").inc()
 
........................................................................ [ 34%]
.F
=================================== FAILURES ===================================
_______ TestCreatePrReviewComment.test_create_pr_review_comment_success ________

self = <tests.test_github_client.TestCreatePrReviewComment object at 0x7fa96a1596d0>

    def test_create_pr_review_comment_success(self):
        """Test successful review comment creation."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_pr = Mock()
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=1, limit=2))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.create_pr_review_comment(
                123456,
                "owner/repo",
                42,
                "Config error",
            )
    
>           assert result is True
E           assert False is True

tests/test_github_client.py:1213: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 14:05:03 [warning  ] Failed to post config error review for PR #42 in owner/repo: unsupported operand type(s) for &: 'float' and 'float' extra={'repo': 'owner/repo', 'pr_number': 42, 'installation_id': 123456, 'error': "unsupported operand type(s) for &: 'float' and 'float'"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestCreatePrReviewComment::test_create_pr_review_comment_success - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 73 passed, 3 deselected in 0.80s
operator: core/ReplaceBinaryOperator_Sub_BitAnd, occurrence: 12
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -568,7 +568,7 @@
                 return True
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() & start_time
                 github_api_request_duration_seconds.labels(operation="comment").observe(duration)
                 github_api_requests_total.labels(operation="comment", status="failure").inc()
 
........................................................................ [ 34%]
..F
=================================== FAILURES ===================================
_______ TestCreatePrReviewComment.test_create_pr_review_comment_failure ________

self = <stampbot.github_client.GitHubAppClient object at 0x7f46051cadb0>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
message = 'Config error'

    def create_pr_review_comment(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        message: str,
    ) -> bool:
        """Create a comment review on a pull request.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            message: Review comment body
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.create_review_comment",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
>               pr = repo.get_pull(pr_number)
                     ^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:545: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_pull' id='139938710145744'>
args = (42,), kwargs = {}, effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestCreatePrReviewComment object at 0x7f460973d810>

    def test_create_pr_review_comment_failure(self):
        """Test review comment creation handles errors."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_pull.side_effect = Exception("API Error")
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.create_pr_review_comment(
                123456,
                "owner/repo",
                42,
                "Config error",
            )

tests/test_github_client.py:1248: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f46051cadb0>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
message = 'Config error'

    def create_pr_review_comment(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        message: str,
    ) -> bool:
        """Create a comment review on a pull request.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            message: Review comment body
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.create_review_comment",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                pr.create_review(body=message, event="COMMENT")
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="comment").observe(duration)
                github_api_requests_total.labels(operation="comment", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.result": "commented"})
                set_span_ok(span)
    
                logger.info(
                    "Posted config error review on PR #%d in %s",
                    pr_number,
                    repo_full_name,
                    extra={
                        "repo": repo_full_name,
                        "pr_number": pr_number,
                        "installation_id": installation_id,
                    },
                )
                return True
    
            except Exception as e:
>               duration = time.time() & start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for &: 'float' and 'float'

stampbot/github_client.py:571: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestCreatePrReviewComment::test_create_pr_review_comment_failure - TypeError: unsupported operand type(s) for &: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 74 passed, 3 deselected in 0.92s
operator: core/ReplaceBinaryOperator_Sub_BitAnd, occurrence: 13
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -619,7 +619,7 @@
                 repo = client.get_repo(repo_full_name)
                 repo.get_label(label_name)
 
-                duration = time.time() - start_time
+                duration = time.time() & start_time
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="success").inc()
 
.....................................................................F
=================================== FAILURES ===================================
__________________ TestRepoHasLabel.test_repo_has_label_true ___________________

self = <tests.test_github_client.TestRepoHasLabel object at 0x7f3ce2efd450>

    def test_repo_has_label_true(self):
        """Test repo_has_label returns True when label exists."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_label.return_value = Mock()
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=1, limit=2))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.repo_has_label(123456, "owner/repo", "autoapprove")
    
>           assert result is True
E           assert None is True

tests/test_github_client.py:1054: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:29:27 [debug    ] Could not verify label autoapprove in owner/repo: unsupported operand type(s) for &: 'float' and 'float' extra={'repo': 'owner/repo', 'label': 'autoapprove', 'installation_id': 123456, 'error': "unsupported operand type(s) for &: 'float' and 'float'"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestRepoHasLabel::test_repo_has_label_true - assert None is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 69 passed, 3 deselected in 0.77s
operator: core/ReplaceBinaryOperator_Sub_BitAnd, occurrence: 14
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -630,7 +630,7 @@
                 return True
 
             except GithubException as e:
-                duration = time.time() - start_time
+                duration = time.time() & start_time
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="failure").inc()
 
......................................................................F
=================================== FAILURES ===================================
________________ TestRepoHasLabel.test_repo_has_label_not_found ________________

self = <stampbot.github_client.GitHubAppClient object at 0x7f977d8513d0>
installation_id = 123456, repo_full_name = 'owner/repo', label_name = 'missing'

    def repo_has_label(
        self,
        installation_id: int,
        repo_full_name: str,
        label_name: str,
    ) -> bool | None:
        """Check whether a repository has a label.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            label_name: Label name to look up
    
        Returns:
            True if label exists, False if not found, None if unknown due to error
        """
        start_time = time.time()
    
        with create_span(
            "github.get_label",
            {
                "github.repo": repo_full_name,
                "github.label": label_name,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
>               repo.get_label(label_name)

stampbot/github_client.py:620: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_label' id='140288622503856'>
args = ('missing',), kwargs = {}
effect = GithubException(404 {"message": "Not Found"})

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               github.GithubException.GithubException: 404 {"message": "Not Found"}

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: GithubException

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestRepoHasLabel object at 0x7f9781a95590>

    def test_repo_has_label_not_found(self):
        """Test repo_has_label returns False when label is missing."""
        from github.GithubException import GithubException
    
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_label.side_effect = GithubException(404, {"message": "Not Found"}, None)
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.repo_has_label(123456, "owner/repo", "missing")
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:1090: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f977d8513d0>
installation_id = 123456, repo_full_name = 'owner/repo', label_name = 'missing'

    def repo_has_label(
        self,
        installation_id: int,
        repo_full_name: str,
        label_name: str,
    ) -> bool | None:
        """Check whether a repository has a label.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            label_name: Label name to look up
    
        Returns:
            True if label exists, False if not found, None if unknown due to error
        """
        start_time = time.time()
    
        with create_span(
            "github.get_label",
            {
                "github.repo": repo_full_name,
                "github.label": label_name,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                repo.get_label(label_name)
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                github_api_requests_total.labels(operation="get_label", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.label_found": True})
                set_span_ok(span)
                return True
    
            except GithubException as e:
>               duration = time.time() & start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for &: 'float' and 'float'

stampbot/github_client.py:633: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestRepoHasLabel::test_repo_has_label_not_found - TypeError: unsupported operand type(s) for &: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 70 passed, 3 deselected in 0.91s
operator: core/ReplaceBinaryOperator_Sub_BitAnd, occurrence: 15
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -655,7 +655,7 @@
                 return None
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() & start_time
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="failure").inc()
 
........................................................................ [ 34%]
F
=================================== FAILURES ===================================
__________________ TestRepoHasLabel.test_repo_has_label_error __________________

self = <stampbot.github_client.GitHubAppClient object at 0x7f779998a5d0>
installation_id = 123456, repo_full_name = 'owner/repo'
label_name = 'autoapprove'

    def repo_has_label(
        self,
        installation_id: int,
        repo_full_name: str,
        label_name: str,
    ) -> bool | None:
        """Check whether a repository has a label.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            label_name: Label name to look up
    
        Returns:
            True if label exists, False if not found, None if unknown due to error
        """
        start_time = time.time()
    
        with create_span(
            "github.get_label",
            {
                "github.repo": repo_full_name,
                "github.label": label_name,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
>               repo.get_label(label_name)

stampbot/github_client.py:620: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_label' id='140151654651216'>
args = ('autoapprove',), kwargs = {}, effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestRepoHasLabel object at 0x7f779dd0a060>

    def test_repo_has_label_error(self):
        """Test repo_has_label returns None on error."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_label.side_effect = Exception("API Error")
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.repo_has_label(123456, "owner/repo", "autoapprove")
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:1164: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f779998a5d0>
installation_id = 123456, repo_full_name = 'owner/repo'
label_name = 'autoapprove'

    def repo_has_label(
        self,
        installation_id: int,
        repo_full_name: str,
        label_name: str,
    ) -> bool | None:
        """Check whether a repository has a label.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            label_name: Label name to look up
    
        Returns:
            True if label exists, False if not found, None if unknown due to error
        """
        start_time = time.time()
    
        with create_span(
            "github.get_label",
            {
                "github.repo": repo_full_name,
                "github.label": label_name,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                repo.get_label(label_name)
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                github_api_requests_total.labels(operation="get_label", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.label_found": True})
                set_span_ok(span)
                return True
    
            except GithubException as e:
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                github_api_requests_total.labels(operation="get_label", status="failure").inc()
    
                if e.status == 404:
                    add_span_attributes(span, {"github.label_found": False})
                    set_span_ok(span)
                    return False
    
                set_span_error(span, e)
                logger.debug(
                    "Could not verify label %s in %s: %s",
                    label_name,
                    repo_full_name,
                    _sanitize_error(e),
                    extra={
                        "repo": repo_full_name,
                        "label": label_name,
                        "installation_id": installation_id,
                        "error": _sanitize_error(e),
                    },
                )
                return None
    
            except Exception as e:
>               duration = time.time() & start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for &: 'float' and 'float'

stampbot/github_client.py:658: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestRepoHasLabel::test_repo_has_label_error - TypeError: unsupported operand type(s) for &: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 72 passed, 3 deselected in 0.91s
operator: core/ReplaceBinaryOperator_Sub_BitAnd, occurrence: 16
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -718,7 +718,7 @@
 
                 has_permission = permission_index >= required_index
 
-                duration = time.time() - start_time
+                duration = time.time() & start_time
                 github_api_request_duration_seconds.labels(operation="get_permission").observe(
                     duration
                 )
............................................................F
=================================== FAILURES ===================================
_____________ TestUserHasPermission.test_user_has_permission_true ______________

self = <tests.test_github_client.TestUserHasPermission object at 0x7f849f3d8f50>

    def test_user_has_permission_true(self):
        """Test required permission satisfied returns True."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_collaborator_permission.return_value = "write"
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=1, limit=2))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.user_has_permission(123456, "owner/repo", "alice", "write")
    
>           assert result is True
E           assert False is True

tests/test_github_client.py:683: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:30:51 [warning  ] Failed to check collaborator permission for alice in owner/repo: unsupported operand type(s) for &: 'float' and 'float' extra={'repo': 'owner/repo', 'username': 'alice', 'installation_id': 123456, 'error': "unsupported operand type(s) for &: 'float' and 'float'"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestUserHasPermission::test_user_has_permission_true - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 60 passed, 3 deselected in 0.75s
operator: core/ReplaceBinaryOperator_Sub_BitAnd, occurrence: 17
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -738,7 +738,7 @@
                 return has_permission
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() & start_time
                 github_api_request_duration_seconds.labels(operation="get_permission").observe(
                     duration
                 )
..............................................................F
=================================== FAILURES ===================================
_____________ TestUserHasPermission.test_user_has_permission_error _____________

self = <stampbot.github_client.GitHubAppClient object at 0x7fe7d8d477d0>
installation_id = 123456, repo_full_name = 'owner/repo', username = 'carol'
required_permission = 'maintain'

    def user_has_permission(
        self,
        installation_id: int,
        repo_full_name: str,
        username: str,
        required_permission: str,
    ) -> bool:
        """Check whether a user has required access to a repository.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            username: GitHub username to check
            required_permission: Minimum required repository permission
    
        Returns:
            True if user meets or exceeds required permission, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.get_collaborator_permission",
            {
                "github.repo": repo_full_name,
                "github.username": username,
                "github.required_permission": required_permission,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
    
>               permission = repo.get_collaborator_permission(username)
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:710: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_collaborator_permission' id='140633751931600'>
args = ('carol',), kwargs = {}, effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestUserHasPermission object at 0x7fe7dcfd5a70>

    def test_user_has_permission_error(self):
        """Test permission check returns False on error."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_collaborator_permission.side_effect = Exception("API Error")
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.user_has_permission(123456, "owner/repo", "carol", "maintain")
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:754: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7fe7d8d477d0>
installation_id = 123456, repo_full_name = 'owner/repo', username = 'carol'
required_permission = 'maintain'

    def user_has_permission(
        self,
        installation_id: int,
        repo_full_name: str,
        username: str,
        required_permission: str,
    ) -> bool:
        """Check whether a user has required access to a repository.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            username: GitHub username to check
            required_permission: Minimum required repository permission
    
        Returns:
            True if user meets or exceeds required permission, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.get_collaborator_permission",
            {
                "github.repo": repo_full_name,
                "github.username": username,
                "github.required_permission": required_permission,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
    
                permission = repo.get_collaborator_permission(username)
                permission_order = ["none", "read", "triage", "write", "maintain", "admin"]
                try:
                    permission_index = permission_order.index(permission)
                    required_index = permission_order.index(required_permission)
                except ValueError:
                    permission_index = -1
                    required_index = 99
    
                has_permission = permission_index >= required_index
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_permission").observe(
                    duration
                )
                github_api_requests_total.labels(operation="get_permission", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(
                    span,
                    {
                        "github.permission": permission,
                        "github.has_permission": has_permission,
                    },
                )
                set_span_ok(span)
    
                return has_permission
    
            except Exception as e:
>               duration = time.time() & start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for &: 'float' and 'float'

stampbot/github_client.py:741: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestUserHasPermission::test_user_has_permission_error - TypeError: unsupported operand type(s) for &: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 62 passed, 3 deselected in 0.88s
operator: core/ReplaceBinaryOperator_Sub_BitAnd, occurrence: 18
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -829,7 +829,7 @@
                         )
                         continue
 
-                duration = time.time() - start_time
+                duration = time.time() & start_time
                 github_api_request_duration_seconds.labels(operation="get_user_teams").observe(
                     duration
                 )
................................................................F
=================================== FAILURES ===================================
____________ TestGetUserTeamSlugs.test_get_user_team_slugs_success _____________

self = <tests.test_github_client.TestGetUserTeamSlugs object at 0x7fd7099811d0>

    def test_get_user_team_slugs_success(self):
        """Test successful team membership check."""
    
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            # Mock user
            mock_user = Mock()
            mock_user.login = "alice"
    
            # Mock team that has alice as member
            mock_team = Mock()
            mock_team.has_in_members.return_value = True
    
            # Mock org
            mock_org = Mock()
            mock_org.get_team_by_slug.return_value = mock_team
    
            mock_github = Mock()
            mock_github.get_organization.return_value = mock_org
            mock_github.get_user.return_value = mock_user
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.get_user_team_slugs(
                123456, "acme", "alice", ["release-team", "deploy-team"]
            )
    
>           assert result == ["release-team", "deploy-team"]
E           AssertionError: assert [] == ['release-tea...'deploy-team']
E             
E             Right contains 2 more items, first extra item: 'release-team'
E             
E             Full diff:
E             + []
E             - [
E             -     'release-team',
E             -     'deploy-team',
E             - ]

tests/test_github_client.py:848: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 14:04:33 [debug    ] User alice is a member of team release-team extra={'org': 'acme', 'username': 'alice', 'team': 'release-team'}
2026-03-27 14:04:33 [debug    ] User alice is a member of team deploy-team extra={'org': 'acme', 'username': 'alice', 'team': 'deploy-team'}
2026-03-27 14:04:33 [warning  ] Failed to check team memberships for alice in acme: unsupported operand type(s) for &: 'float' and 'float' extra={'org': 'acme', 'username': 'alice', 'installation_id': 123456, 'error': "unsupported operand type(s) for &: 'float' and 'float'"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetUserTeamSlugs::test_get_user_team_slugs_success - AssertionError: assert [] == ['release-tea...'deploy-team']
  
  Right contains 2 more items, first extra item: 'release-team'
  
  Full diff:
  + []
  - [
  -     'release-team',
  -     'deploy-team',
  - ]
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 64 passed, 3 deselected in 0.78s
operator: core/ReplaceBinaryOperator_Sub_BitAnd, occurrence: 19
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -849,7 +849,7 @@
                 return member_teams
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() & start_time
                 github_api_request_duration_seconds.labels(operation="get_user_teams").observe(
                     duration
                 )
....................................................................F
=================================== FAILURES ===================================
_______ TestGetUserTeamSlugs.test_get_user_team_slugs_general_exception ________

self = <stampbot.github_client.GitHubAppClient object at 0x7f35df72b1d0>
installation_id = 123456, org_name = 'acme', username = 'alice'
allowed_teams = ['release-team']

    def get_user_team_slugs(
        self,
        installation_id: int,
        org_name: str,
        username: str,
        allowed_teams: list[str],
    ) -> list[str]:
        """Get team slugs the user is a member of from the allowed teams list.
    
        Args:
            installation_id: GitHub App installation ID
            org_name: Organization name
            username: GitHub username to check
            allowed_teams: List of team slugs to check (can be "org/team" or just "team")
    
        Returns:
            List of team slugs the user is a member of
        """
        start_time = time.time()
    
        with create_span(
            "github.get_user_team_slugs",
            {
                "github.org": org_name,
                "github.username": username,
                "github.installation_id": installation_id,
                "github.teams_to_check": len(allowed_teams),
            },
        ) as span:
            member_teams: list[str] = []
    
            try:
                client = self._get_installation_client(installation_id)
>               org = client.get_organization(org_name)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:796: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_organization' id='139869360650096'>
args = ('acme',), kwargs = {}, effect = Exception('Network error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: Network error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestGetUserTeamSlugs object at 0x7f35e3e045f0>

    def test_get_user_team_slugs_general_exception(self):
        """Test team membership check with general exception."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_github = Mock()
            mock_github.get_organization.side_effect = Exception("Network error")
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.get_user_team_slugs(123456, "acme", "alice", ["release-team"])
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:1010: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f35df72b1d0>
installation_id = 123456, org_name = 'acme', username = 'alice'
allowed_teams = ['release-team']

    def get_user_team_slugs(
        self,
        installation_id: int,
        org_name: str,
        username: str,
        allowed_teams: list[str],
    ) -> list[str]:
        """Get team slugs the user is a member of from the allowed teams list.
    
        Args:
            installation_id: GitHub App installation ID
            org_name: Organization name
            username: GitHub username to check
            allowed_teams: List of team slugs to check (can be "org/team" or just "team")
    
        Returns:
            List of team slugs the user is a member of
        """
        start_time = time.time()
    
        with create_span(
            "github.get_user_team_slugs",
            {
                "github.org": org_name,
                "github.username": username,
                "github.installation_id": installation_id,
                "github.teams_to_check": len(allowed_teams),
            },
        ) as span:
            member_teams: list[str] = []
    
            try:
                client = self._get_installation_client(installation_id)
                org = client.get_organization(org_name)
    
                for team_ref in allowed_teams:
                    # Extract team slug (handle both "org/team" and "team" formats)
                    team_slug = team_ref.split("/")[-1] if "/" in team_ref else team_ref
    
                    try:
                        team = org.get_team_by_slug(team_slug)
                        # Check if user is a member
                        user = client.get_user(username)
                        if team.has_in_members(user):  # type: ignore[arg-type]
                            member_teams.append(team_slug)
                            logger.debug(
                                "User %s is a member of team %s",
                                username,
                                team_slug,
                                extra={
                                    "org": org_name,
                                    "username": username,
                                    "team": team_slug,
                                },
                            )
                    except GithubException as e:
                        # Team not found or no access - skip silently
                        logger.debug(
                            "Could not check team %s membership: %s",
                            team_slug,
                            e.data.get("message", str(e)) if hasattr(e, "data") else str(e),
                            extra={
                                "org": org_name,
                                "team": team_slug,
                                "username": username,
                            },
                        )
                        continue
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_user_teams").observe(
                    duration
                )
                github_api_requests_total.labels(operation="get_user_teams", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(
                    span,
                    {
                        "github.member_teams": len(member_teams),
                        "github.teams_checked": len(allowed_teams),
                    },
                )
                set_span_ok(span)
    
                return member_teams
    
            except Exception as e:
>               duration = time.time() & start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for &: 'float' and 'float'

stampbot/github_client.py:852: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetUserTeamSlugs::test_get_user_team_slugs_general_exception - TypeError: unsupported operand type(s) for &: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 68 passed, 3 deselected in 0.90s
operator: core/ReplaceBinaryOperator_Sub_BitXor, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -168,7 +168,7 @@
                     retry=retry,
                 )
 
-                duration = time.time() - start_time
+                duration = time.time() ^ start_time
                 github_api_request_duration_seconds.labels(operation="get_token").observe(duration)
                 github_api_requests_total.labels(operation="get_token", status="success").inc()
 
...............................................F
=================================== FAILURES ===================================
________ TestGetInstallationClient.test_get_installation_client_success ________

self = <tests.test_github_client.TestGetInstallationClient object at 0x7f406aca8050>

    def test_get_installation_client_success(self):
        """Test successful installation client creation."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client._get_installation_client(123456)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:211: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f4066c8c830>
installation_id = 123456

    def _get_installation_client(self, installation_id: int) -> Github:
        """Get authenticated GitHub client for an installation.
    
        Args:
            installation_id: GitHub App installation ID.
    
        Returns:
            Authenticated Github client instance with timeout and retry configured.
    
        Raises:
            github.GithubException: If token exchange fails.
        """
        start_time = time.time()
    
        with create_span(
            "github.get_installation_token",
            {"github.installation_id": installation_id},
        ) as span:
            try:
                auth = self.integration.get_access_token(installation_id)
    
                # Configure retry with exponential backoff
                retry = Retry(
                    total=GITHUB_API_RETRY_TOTAL,
                    backoff_factor=GITHUB_API_RETRY_BACKOFF,
                    status_forcelist=[500, 502, 503, 504],
                    allowed_methods=["GET", "POST", "PUT", "DELETE", "PATCH"],
                )
    
                client = Github(
                    auth=Auth.Token(auth.token),
                    timeout=GITHUB_API_TIMEOUT,
                    retry=retry,
                )
    
>               duration = time.time() ^ start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for ^: 'float' and 'float'

stampbot/github_client.py:171: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetInstallationClient::test_get_installation_client_success - TypeError: unsupported operand type(s) for ^: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 47 passed, 3 deselected in 0.76s
operator: core/ReplaceBinaryOperator_Sub_BitXor, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -176,7 +176,7 @@
                 return client
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() ^ start_time
                 github_api_request_duration_seconds.labels(operation="get_token").observe(duration)
                 github_api_requests_total.labels(operation="get_token", status="failure").inc()
                 set_span_error(span, e)
................................................F
=================================== FAILURES ===================================
________ TestGetInstallationClient.test_get_installation_client_failure ________

self = <stampbot.github_client.GitHubAppClient object at 0x7fdf399d9230>
installation_id = 123456

    def _get_installation_client(self, installation_id: int) -> Github:
        """Get authenticated GitHub client for an installation.
    
        Args:
            installation_id: GitHub App installation ID.
    
        Returns:
            Authenticated Github client instance with timeout and retry configured.
    
        Raises:
            github.GithubException: If token exchange fails.
        """
        start_time = time.time()
    
        with create_span(
            "github.get_installation_token",
            {"github.installation_id": installation_id},
        ) as span:
            try:
>               auth = self.integration.get_access_token(installation_id)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:155: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='GithubIntegration().get_access_token' id='140596701642240'>
args = (123456,), kwargs = {}, effect = Exception('Token exchange failed')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: Token exchange failed

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestGetInstallationClient object at 0x7fdf3c608190>

    def test_get_installation_client_failure(self):
        """Test installation client creation failure."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_integration.get_access_token.side_effect = Exception("Token exchange failed")
            mock_integration_cls.return_value = mock_integration
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            with pytest.raises(Exception, match="Token exchange failed"):
>               client._get_installation_client(123456)

tests/test_github_client.py:240: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7fdf399d9230>
installation_id = 123456

    def _get_installation_client(self, installation_id: int) -> Github:
        """Get authenticated GitHub client for an installation.
    
        Args:
            installation_id: GitHub App installation ID.
    
        Returns:
            Authenticated Github client instance with timeout and retry configured.
    
        Raises:
            github.GithubException: If token exchange fails.
        """
        start_time = time.time()
    
        with create_span(
            "github.get_installation_token",
            {"github.installation_id": installation_id},
        ) as span:
            try:
                auth = self.integration.get_access_token(installation_id)
    
                # Configure retry with exponential backoff
                retry = Retry(
                    total=GITHUB_API_RETRY_TOTAL,
                    backoff_factor=GITHUB_API_RETRY_BACKOFF,
                    status_forcelist=[500, 502, 503, 504],
                    allowed_methods=["GET", "POST", "PUT", "DELETE", "PATCH"],
                )
    
                client = Github(
                    auth=Auth.Token(auth.token),
                    timeout=GITHUB_API_TIMEOUT,
                    retry=retry,
                )
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_token").observe(duration)
                github_api_requests_total.labels(operation="get_token", status="success").inc()
    
                set_span_ok(span)
                return client
    
            except Exception as e:
>               duration = time.time() ^ start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for ^: 'float' and 'float'

stampbot/github_client.py:179: TypeError

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestGetInstallationClient object at 0x7fdf3c608190>

    def test_get_installation_client_failure(self):
        """Test installation client creation failure."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_integration.get_access_token.side_effect = Exception("Token exchange failed")
            mock_integration_cls.return_value = mock_integration
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           with pytest.raises(Exception, match="Token exchange failed"):
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E           AssertionError: Regex pattern did not match.
E             Expected regex: 'Token exchange failed'
E             Actual message: "unsupported operand type(s) for ^: 'float' and 'float'"

tests/test_github_client.py:239: AssertionError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetInstallationClient::test_get_installation_client_failure - AssertionError: Regex pattern did not match.
  Expected regex: 'Token exchange failed'
  Actual message: "unsupported operand type(s) for ^: 'float' and 'float'"
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 48 passed, 3 deselected in 0.85s
operator: core/ReplaceBinaryOperator_Sub_BitXor, occurrence: 2
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -240,7 +240,7 @@
                     event="APPROVE",
                 )
 
-                duration = time.time() - start_time
+                duration = time.time() ^ start_time
                 github_api_request_duration_seconds.labels(operation="approve").observe(duration)
                 github_api_requests_total.labels(operation="approve", status="success").inc()
 
...................................................F
=================================== FAILURES ===================================
____________________ TestApprovePR.test_approve_pr_success _____________________

self = <tests.test_github_client.TestApprovePR object at 0x7fe7ab684550>

    def test_approve_pr_success(self):
        """Test successful PR approval."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_pr = Mock()
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.approve_pr(123456, "owner/repo", 42)
    
>           assert result is True
E           assert False is True

tests/test_github_client.py:313: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 14:05:22 [error    ] Failed to approve PR #42 in owner/repo: unsupported operand type(s) for ^: 'float' and 'float' extra={'repo': 'owner/repo', 'pr_number': 42, 'installation_id': 123456, 'error': "unsupported operand type(s) for ^: 'float' and 'float'"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestApprovePR::test_approve_pr_success - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 51 passed, 3 deselected in 0.73s
operator: core/ReplaceBinaryOperator_Sub_BitXor, occurrence: 3
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -260,7 +260,7 @@
                 return True
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() ^ start_time
                 github_api_request_duration_seconds.labels(operation="approve").observe(duration)
                 github_api_requests_total.labels(operation="approve", status="failure").inc()
 
....................................................F
=================================== FAILURES ===================================
____________________ TestApprovePR.test_approve_pr_failure _____________________

self = <stampbot.github_client.GitHubAppClient object at 0x7f9f0b45ac50>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
comment = 'Auto-approved by Stampbot'

    def approve_pr(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        comment: str = "Auto-approved by Stampbot",
    ) -> bool:
        """Approve a pull request.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            comment: Review comment
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.approve_pr",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                # Create approval review
>               pr.create_review(
                    body=comment,
                    event="APPROVE",
                )

stampbot/github_client.py:238: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_pull().create_review' id='140321066055872'>
args = (), kwargs = {'body': 'Auto-approved by Stampbot', 'event': 'APPROVE'}
effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestApprovePR object at 0x7f9f0f6e8690>

    def test_approve_pr_failure(self):
        """Test PR approval failure."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_pr = Mock()
            mock_pr.create_review.side_effect = Exception("API Error")
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.approve_pr(123456, "owner/repo", 42)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:350: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f9f0b45ac50>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
comment = 'Auto-approved by Stampbot'

    def approve_pr(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        comment: str = "Auto-approved by Stampbot",
    ) -> bool:
        """Approve a pull request.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            comment: Review comment
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.approve_pr",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                # Create approval review
                pr.create_review(
                    body=comment,
                    event="APPROVE",
                )
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="approve").observe(duration)
                github_api_requests_total.labels(operation="approve", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.result": "approved"})
                set_span_ok(span)
    
                logger.info(
                    f"Approved PR #{pr_number} in {repo_full_name}",
                    extra={
                        "repo": repo_full_name,
                        "pr_number": pr_number,
                        "installation_id": installation_id,
                    },
                )
                return True
    
            except Exception as e:
>               duration = time.time() ^ start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for ^: 'float' and 'float'

stampbot/github_client.py:263: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestApprovePR::test_approve_pr_failure - TypeError: unsupported operand type(s) for ^: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 52 passed, 3 deselected in 0.85s
operator: core/ReplaceBinaryOperator_Sub_BitXor, occurrence: 4
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -317,7 +317,7 @@
                 review = pr.get_review(review_id)
                 review.dismiss(message)
 
-                duration = time.time() - start_time
+                duration = time.time() ^ start_time
                 github_api_request_duration_seconds.labels(operation="dismiss").observe(duration)
                 github_api_requests_total.labels(operation="dismiss", status="success").inc()
 
.....................................................F
=================================== FAILURES ===================================
______________ TestDismissApproval.test_dismiss_approval_success _______________

self = <tests.test_github_client.TestDismissApproval object at 0x7fb7c96d07d0>

    def test_dismiss_approval_success(self):
        """Test successful approval dismissal."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_review = Mock()
            mock_pr = Mock()
            mock_pr.get_review.return_value = mock_review
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.dismiss_approval(123456, "owner/repo", 42, 999)
    
>           assert result is True
E           assert False is True

tests/test_github_client.py:396: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:36:23 [error    ] Failed to dismiss approval for PR #42 in owner/repo: unsupported operand type(s) for ^: 'float' and 'float' extra={'repo': 'owner/repo', 'pr_number': 42, 'installation_id': 123456, 'error': "unsupported operand type(s) for ^: 'float' and 'float'"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestDismissApproval::test_dismiss_approval_success - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 53 passed, 3 deselected in 0.74s
operator: core/ReplaceBinaryOperator_Sub_BitXor, occurrence: 5
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -338,7 +338,7 @@
                 return True
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() ^ start_time
                 github_api_request_duration_seconds.labels(operation="dismiss").observe(duration)
                 github_api_requests_total.labels(operation="dismiss", status="failure").inc()
 
......................................................F
=================================== FAILURES ===================================
______________ TestDismissApproval.test_dismiss_approval_failure _______________

self = <stampbot.github_client.GitHubAppClient object at 0x7f43d0af6850>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
review_id = 999, message = 'Approval dismissed by Stampbot'

    def dismiss_approval(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        review_id: int,
        message: str = "Approval dismissed by Stampbot",
    ) -> bool:
        """Dismiss a pull request approval.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            review_id: Review ID to dismiss
            message: Dismissal message
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.dismiss_approval",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.review_id": review_id,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                # Get the review and dismiss it
                review = pr.get_review(review_id)
>               review.dismiss(message)

stampbot/github_client.py:318: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_pull().get_review().dismiss' id='139929239548800'>
args = ('Approval dismissed by Stampbot',), kwargs = {}
effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestDismissApproval object at 0x7f43d4dbc910>

    def test_dismiss_approval_failure(self):
        """Test approval dismissal failure."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_review = Mock()
            mock_review.dismiss.side_effect = Exception("API Error")
            mock_pr = Mock()
            mock_pr.get_review.return_value = mock_review
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.dismiss_approval(123456, "owner/repo", 42, 999)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:435: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f43d0af6850>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
review_id = 999, message = 'Approval dismissed by Stampbot'

    def dismiss_approval(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        review_id: int,
        message: str = "Approval dismissed by Stampbot",
    ) -> bool:
        """Dismiss a pull request approval.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            review_id: Review ID to dismiss
            message: Dismissal message
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.dismiss_approval",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.review_id": review_id,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                # Get the review and dismiss it
                review = pr.get_review(review_id)
                review.dismiss(message)
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="dismiss").observe(duration)
                github_api_requests_total.labels(operation="dismiss", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.result": "dismissed"})
                set_span_ok(span)
    
                logger.info(
                    f"Dismissed approval for PR #{pr_number} in {repo_full_name}",
                    extra={
                        "repo": repo_full_name,
                        "pr_number": pr_number,
                        "installation_id": installation_id,
                        "review_id": review_id,
                    },
                )
                return True
    
            except Exception as e:
>               duration = time.time() ^ start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for ^: 'float' and 'float'

stampbot/github_client.py:341: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestDismissApproval::test_dismiss_approval_failure - TypeError: unsupported operand type(s) for ^: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 54 passed, 3 deselected in 0.89s
operator: core/ReplaceBinaryOperator_Sub_BitXor, occurrence: 6
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -393,7 +393,7 @@
 
                 content = repo.get_contents(file_path, ref=ref)  # type: ignore[arg-type]
                 if isinstance(content, list):
-                    duration = time.time() - start_time
+                    duration = time.time() ^ start_time
                     github_api_request_duration_seconds.labels(operation="get_file").observe(
                         duration
                     )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_BitXor, occurrence: 7
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -402,7 +402,7 @@
                     set_span_ok(span)
                     return None
 
-                duration = time.time() - start_time
+                duration = time.time() ^ start_time
                 github_api_request_duration_seconds.labels(operation="get_file").observe(duration)
                 github_api_requests_total.labels(operation="get_file", status="success").inc()
 
.......................................................F
=================================== FAILURES ===================================
__________________ TestGetRepoFile.test_get_repo_file_success __________________

self = <tests.test_github_client.TestGetRepoFile object at 0x7f5e4d3f8a50>

    def test_get_repo_file_success(self):
        """Test successful file retrieval."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_content = Mock()
            mock_content.decoded_content = b"file content"
            mock_repo = Mock()
            mock_repo.get_contents.return_value = mock_content
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.get_repo_file(123456, "owner/repo", "stampbot.toml")
    
>           assert result == "file content"
E           AssertionError: assert None == 'file content'

tests/test_github_client.py:480: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:47:38 [debug    ] Could not fetch stampbot.toml from owner/repo: unsupported operand type(s) for ^: 'float' and 'float' extra={'repo': 'owner/repo', 'file_path': 'stampbot.toml', 'installation_id': 123456}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetRepoFile::test_get_repo_file_success - AssertionError: assert None == 'file content'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 55 passed, 3 deselected in 0.75s
operator: core/ReplaceBinaryOperator_Sub_BitXor, occurrence: 8
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -414,7 +414,7 @@
                 return content.decoded_content.decode("utf-8")
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() ^ start_time
                 github_api_request_duration_seconds.labels(operation="get_file").observe(duration)
                 github_api_requests_total.labels(operation="get_file", status="not_found").inc()
 
.........................................................F
=================================== FAILURES ===================================
___________ TestGetRepoFile.test_get_repo_file_returns_none_on_error ___________

self = <stampbot.github_client.GitHubAppClient object at 0x7f343f350cd0>
installation_id = 123456, repo_full_name = 'owner/repo'
file_path = 'nonexistent.toml', ref = None

    def get_repo_file(
        self,
        installation_id: int,
        repo_full_name: str,
        file_path: str,
        ref: str | None = None,
    ) -> str | None:
        """Get file content from repository.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            file_path: Path to file in repository
            ref: Git reference (branch, tag, commit). Defaults to default branch
    
        Returns:
            File content as string, or None if not found
        """
        start_time = time.time()
    
        with create_span(
            "github.get_file",
            {
                "github.repo": repo_full_name,
                "github.file_path": file_path,
                "github.ref": ref or "default",
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
    
>               content = repo.get_contents(file_path, ref=ref)  # type: ignore[arg-type]
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:394: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_contents' id='139862375777680'>
args = ('nonexistent.toml',), kwargs = {'ref': None}
effect = Exception('File not found')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: File not found

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestGetRepoFile object at 0x7f3443721940>

    def test_get_repo_file_returns_none_on_error(self):
        """Test that get_repo_file returns None when file not found."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_contents.side_effect = Exception("File not found")
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.get_repo_file(123456, "owner/repo", "nonexistent.toml")
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:551: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f343f350cd0>
installation_id = 123456, repo_full_name = 'owner/repo'
file_path = 'nonexistent.toml', ref = None

    def get_repo_file(
        self,
        installation_id: int,
        repo_full_name: str,
        file_path: str,
        ref: str | None = None,
    ) -> str | None:
        """Get file content from repository.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            file_path: Path to file in repository
            ref: Git reference (branch, tag, commit). Defaults to default branch
    
        Returns:
            File content as string, or None if not found
        """
        start_time = time.time()
    
        with create_span(
            "github.get_file",
            {
                "github.repo": repo_full_name,
                "github.file_path": file_path,
                "github.ref": ref or "default",
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
    
                content = repo.get_contents(file_path, ref=ref)  # type: ignore[arg-type]
                if isinstance(content, list):
                    duration = time.time() - start_time
                    github_api_request_duration_seconds.labels(operation="get_file").observe(
                        duration
                    )
                    github_api_requests_total.labels(operation="get_file", status="not_found").inc()
                    add_span_attributes(span, {"github.result": "not_found"})
                    set_span_ok(span)
                    return None
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_file").observe(duration)
                github_api_requests_total.labels(operation="get_file", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.result": "found"})
                set_span_ok(span)
    
                return content.decoded_content.decode("utf-8")
    
            except Exception as e:
>               duration = time.time() ^ start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for ^: 'float' and 'float'

stampbot/github_client.py:417: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetRepoFile::test_get_repo_file_returns_none_on_error - TypeError: unsupported operand type(s) for ^: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 57 passed, 3 deselected in 0.87s
operator: core/ReplaceBinaryOperator_Sub_BitXor, occurrence: 9
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -473,7 +473,7 @@
                     if review.user.login == bot_user and review.state == "APPROVED":
                         bot_review_ids.append(review.id)
 
-                duration = time.time() - start_time
+                duration = time.time() ^ start_time
                 github_api_request_duration_seconds.labels(operation="find_reviews").observe(
                     duration
                 )
..........................................................F
=================================== FAILURES ===================================
_______________ TestFindBotReviews.test_find_bot_reviews_success _______________

self = <tests.test_github_client.TestFindBotReviews object at 0x7f6a44f94cd0>

    def test_find_bot_reviews_success(self):
        """Test successful bot review finding."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration.get_app.return_value = Mock(slug="stampbot")
            mock_integration_cls.return_value = mock_integration
    
            # Create mock reviews
            bot_review = Mock()
            bot_review.user.login = "stampbot[bot]"
            bot_review.state = "APPROVED"
            bot_review.id = 123
    
            other_review = Mock()
            other_review.user.login = "other-user"
            other_review.state = "APPROVED"
            other_review.id = 456
    
            mock_pr = Mock()
            mock_pr.get_reviews.return_value = [bot_review, other_review]
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.find_bot_reviews(123456, "owner/repo", 42)
    
>           assert result == [123]
E           assert [] == [123]
E             
E             Right contains one more item: 123
E             
E             Full diff:
E             + []
E             - [
E             -     123,
E             - ]

tests/test_github_client.py:608: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:55:55 [error    ] Failed to find bot reviews for PR #42 in owner/repo: unsupported operand type(s) for ^: 'float' and 'float' extra={'repo': 'owner/repo', 'pr_number': 42, 'installation_id': 123456, 'error': "unsupported operand type(s) for ^: 'float' and 'float'"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestFindBotReviews::test_find_bot_reviews_success - assert [] == [123]
  
  Right contains one more item: 123
  
  Full diff:
  + []
  - [
  -     123,
  - ]
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 58 passed, 3 deselected in 0.75s
operator: core/ReplaceBinaryOperator_Sub_BitXor, occurrence: 10
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -489,7 +489,7 @@
                 return bot_review_ids
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() ^ start_time
                 github_api_request_duration_seconds.labels(operation="find_reviews").observe(
                     duration
                 )
...........................................................F
=================================== FAILURES ===================================
_______ TestFindBotReviews.test_find_bot_reviews_returns_empty_on_error ________

self = <stampbot.github_client.GitHubAppClient object at 0x7f876e784f30>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42

    def find_bot_reviews(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
    ) -> list[int]:
        """Find all reviews created by this bot on a PR.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
    
        Returns:
            List of review IDs created by the bot
        """
        start_time = time.time()
    
        with create_span(
            "github.find_bot_reviews",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
>               repo = client.get_repo(repo_full_name)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:462: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo' id='140219651162528'>
args = ('owner/repo',), kwargs = {}, effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestFindBotReviews object at 0x7f8772708e10>

    def test_find_bot_reviews_returns_empty_on_error(self):
        """Test that find_bot_reviews returns empty list on error."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_github = Mock()
            mock_github.get_repo.side_effect = Exception("API Error")
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.find_bot_reviews(123456, "owner/repo", 42)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:640: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f876e784f30>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42

    def find_bot_reviews(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
    ) -> list[int]:
        """Find all reviews created by this bot on a PR.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
    
        Returns:
            List of review IDs created by the bot
        """
        start_time = time.time()
    
        with create_span(
            "github.find_bot_reviews",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                # Get bot user via JWT-authenticated integration (installation tokens
                # cannot call GET /user, which is restricted by GitHub Apps integration)
                app_info = self.integration.get_app()
                bot_user = f"{app_info.slug}[bot]"
    
                # Find all reviews by bot that are approvals
                bot_review_ids = []
                for review in pr.get_reviews():
                    if review.user.login == bot_user and review.state == "APPROVED":
                        bot_review_ids.append(review.id)
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="find_reviews").observe(
                    duration
                )
                github_api_requests_total.labels(operation="find_reviews", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(
                    span, {"github.reviews_found": len(bot_review_ids), "github.bot_user": bot_user}
                )
                set_span_ok(span)
    
                return bot_review_ids
    
            except Exception as e:
>               duration = time.time() ^ start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for ^: 'float' and 'float'

stampbot/github_client.py:492: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestFindBotReviews::test_find_bot_reviews_returns_empty_on_error - TypeError: unsupported operand type(s) for ^: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 59 passed, 3 deselected in 0.88s
operator: core/ReplaceBinaryOperator_Sub_BitXor, occurrence: 11
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -546,7 +546,7 @@
 
                 pr.create_review(body=message, event="COMMENT")
 
-                duration = time.time() - start_time
+                duration = time.time() ^ start_time
                 github_api_request_duration_seconds.labels(operation="comment").observe(duration)
                 github_api_requests_total.labels(operation="comment", status="success").inc()
 
........................................................................ [ 34%]
.F
=================================== FAILURES ===================================
_______ TestCreatePrReviewComment.test_create_pr_review_comment_success ________

self = <tests.test_github_client.TestCreatePrReviewComment object at 0x7fd8843956d0>

    def test_create_pr_review_comment_success(self):
        """Test successful review comment creation."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_pr = Mock()
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=1, limit=2))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.create_pr_review_comment(
                123456,
                "owner/repo",
                42,
                "Config error",
            )
    
>           assert result is True
E           assert False is True

tests/test_github_client.py:1213: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 14:12:41 [warning  ] Failed to post config error review for PR #42 in owner/repo: unsupported operand type(s) for ^: 'float' and 'float' extra={'repo': 'owner/repo', 'pr_number': 42, 'installation_id': 123456, 'error': "unsupported operand type(s) for ^: 'float' and 'float'"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestCreatePrReviewComment::test_create_pr_review_comment_success - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 73 passed, 3 deselected in 0.78s
operator: core/ReplaceBinaryOperator_Sub_BitXor, occurrence: 12
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -568,7 +568,7 @@
                 return True
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() ^ start_time
                 github_api_request_duration_seconds.labels(operation="comment").observe(duration)
                 github_api_requests_total.labels(operation="comment", status="failure").inc()
 
........................................................................ [ 34%]
..F
=================================== FAILURES ===================================
_______ TestCreatePrReviewComment.test_create_pr_review_comment_failure ________

self = <stampbot.github_client.GitHubAppClient object at 0x7f2016cc9d30>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
message = 'Config error'

    def create_pr_review_comment(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        message: str,
    ) -> bool:
        """Create a comment review on a pull request.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            message: Review comment body
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.create_review_comment",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
>               pr = repo.get_pull(pr_number)
                     ^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:545: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_pull' id='139775798135504'>
args = (42,), kwargs = {}, effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestCreatePrReviewComment object at 0x7f201b2e1810>

    def test_create_pr_review_comment_failure(self):
        """Test review comment creation handles errors."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_pull.side_effect = Exception("API Error")
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.create_pr_review_comment(
                123456,
                "owner/repo",
                42,
                "Config error",
            )

tests/test_github_client.py:1248: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f2016cc9d30>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
message = 'Config error'

    def create_pr_review_comment(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        message: str,
    ) -> bool:
        """Create a comment review on a pull request.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            message: Review comment body
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.create_review_comment",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                pr.create_review(body=message, event="COMMENT")
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="comment").observe(duration)
                github_api_requests_total.labels(operation="comment", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.result": "commented"})
                set_span_ok(span)
    
                logger.info(
                    "Posted config error review on PR #%d in %s",
                    pr_number,
                    repo_full_name,
                    extra={
                        "repo": repo_full_name,
                        "pr_number": pr_number,
                        "installation_id": installation_id,
                    },
                )
                return True
    
            except Exception as e:
>               duration = time.time() ^ start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for ^: 'float' and 'float'

stampbot/github_client.py:571: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestCreatePrReviewComment::test_create_pr_review_comment_failure - TypeError: unsupported operand type(s) for ^: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 74 passed, 3 deselected in 0.90s
operator: core/ReplaceBinaryOperator_Sub_BitXor, occurrence: 13
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -619,7 +619,7 @@
                 repo = client.get_repo(repo_full_name)
                 repo.get_label(label_name)
 
-                duration = time.time() - start_time
+                duration = time.time() ^ start_time
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="success").inc()
 
.....................................................................F
=================================== FAILURES ===================================
__________________ TestRepoHasLabel.test_repo_has_label_true ___________________

self = <tests.test_github_client.TestRepoHasLabel object at 0x7fb34383d450>

    def test_repo_has_label_true(self):
        """Test repo_has_label returns True when label exists."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_label.return_value = Mock()
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=1, limit=2))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.repo_has_label(123456, "owner/repo", "autoapprove")
    
>           assert result is True
E           assert None is True

tests/test_github_client.py:1054: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:39:12 [debug    ] Could not verify label autoapprove in owner/repo: unsupported operand type(s) for ^: 'float' and 'float' extra={'repo': 'owner/repo', 'label': 'autoapprove', 'installation_id': 123456, 'error': "unsupported operand type(s) for ^: 'float' and 'float'"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestRepoHasLabel::test_repo_has_label_true - assert None is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 69 passed, 3 deselected in 0.78s
operator: core/ReplaceBinaryOperator_Sub_BitXor, occurrence: 14
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -630,7 +630,7 @@
                 return True
 
             except GithubException as e:
-                duration = time.time() - start_time
+                duration = time.time() ^ start_time
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="failure").inc()
 
......................................................................F
=================================== FAILURES ===================================
________________ TestRepoHasLabel.test_repo_has_label_not_found ________________

self = <stampbot.github_client.GitHubAppClient object at 0x7fe6e4e50650>
installation_id = 123456, repo_full_name = 'owner/repo', label_name = 'missing'

    def repo_has_label(
        self,
        installation_id: int,
        repo_full_name: str,
        label_name: str,
    ) -> bool | None:
        """Check whether a repository has a label.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            label_name: Label name to look up
    
        Returns:
            True if label exists, False if not found, None if unknown due to error
        """
        start_time = time.time()
    
        with create_span(
            "github.get_label",
            {
                "github.repo": repo_full_name,
                "github.label": label_name,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
>               repo.get_label(label_name)

stampbot/github_client.py:620: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_label' id='140629659264944'>
args = ('missing',), kwargs = {}
effect = GithubException(404 {"message": "Not Found"})

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               github.GithubException.GithubException: 404 {"message": "Not Found"}

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: GithubException

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestRepoHasLabel object at 0x7fe6e9595590>

    def test_repo_has_label_not_found(self):
        """Test repo_has_label returns False when label is missing."""
        from github.GithubException import GithubException
    
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_label.side_effect = GithubException(404, {"message": "Not Found"}, None)
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.repo_has_label(123456, "owner/repo", "missing")
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:1090: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7fe6e4e50650>
installation_id = 123456, repo_full_name = 'owner/repo', label_name = 'missing'

    def repo_has_label(
        self,
        installation_id: int,
        repo_full_name: str,
        label_name: str,
    ) -> bool | None:
        """Check whether a repository has a label.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            label_name: Label name to look up
    
        Returns:
            True if label exists, False if not found, None if unknown due to error
        """
        start_time = time.time()
    
        with create_span(
            "github.get_label",
            {
                "github.repo": repo_full_name,
                "github.label": label_name,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                repo.get_label(label_name)
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                github_api_requests_total.labels(operation="get_label", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.label_found": True})
                set_span_ok(span)
                return True
    
            except GithubException as e:
>               duration = time.time() ^ start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for ^: 'float' and 'float'

stampbot/github_client.py:633: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestRepoHasLabel::test_repo_has_label_not_found - TypeError: unsupported operand type(s) for ^: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 70 passed, 3 deselected in 0.91s
operator: core/ReplaceBinaryOperator_Sub_BitXor, occurrence: 15
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -655,7 +655,7 @@
                 return None
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() ^ start_time
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="failure").inc()
 
........................................................................ [ 34%]
F
=================================== FAILURES ===================================
__________________ TestRepoHasLabel.test_repo_has_label_error __________________

self = <stampbot.github_client.GitHubAppClient object at 0x7f519e07fef0>
installation_id = 123456, repo_full_name = 'owner/repo'
label_name = 'autoapprove'

    def repo_has_label(
        self,
        installation_id: int,
        repo_full_name: str,
        label_name: str,
    ) -> bool | None:
        """Check whether a repository has a label.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            label_name: Label name to look up
    
        Returns:
            True if label exists, False if not found, None if unknown due to error
        """
        start_time = time.time()
    
        with create_span(
            "github.get_label",
            {
                "github.repo": repo_full_name,
                "github.label": label_name,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
>               repo.get_label(label_name)

stampbot/github_client.py:620: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_label' id='139988520293712'>
args = ('autoapprove',), kwargs = {}, effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestRepoHasLabel object at 0x7f51a25d2060>

    def test_repo_has_label_error(self):
        """Test repo_has_label returns None on error."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_label.side_effect = Exception("API Error")
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.repo_has_label(123456, "owner/repo", "autoapprove")
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:1164: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f519e07fef0>
installation_id = 123456, repo_full_name = 'owner/repo'
label_name = 'autoapprove'

    def repo_has_label(
        self,
        installation_id: int,
        repo_full_name: str,
        label_name: str,
    ) -> bool | None:
        """Check whether a repository has a label.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            label_name: Label name to look up
    
        Returns:
            True if label exists, False if not found, None if unknown due to error
        """
        start_time = time.time()
    
        with create_span(
            "github.get_label",
            {
                "github.repo": repo_full_name,
                "github.label": label_name,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                repo.get_label(label_name)
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                github_api_requests_total.labels(operation="get_label", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.label_found": True})
                set_span_ok(span)
                return True
    
            except GithubException as e:
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                github_api_requests_total.labels(operation="get_label", status="failure").inc()
    
                if e.status == 404:
                    add_span_attributes(span, {"github.label_found": False})
                    set_span_ok(span)
                    return False
    
                set_span_error(span, e)
                logger.debug(
                    "Could not verify label %s in %s: %s",
                    label_name,
                    repo_full_name,
                    _sanitize_error(e),
                    extra={
                        "repo": repo_full_name,
                        "label": label_name,
                        "installation_id": installation_id,
                        "error": _sanitize_error(e),
                    },
                )
                return None
    
            except Exception as e:
>               duration = time.time() ^ start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for ^: 'float' and 'float'

stampbot/github_client.py:658: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestRepoHasLabel::test_repo_has_label_error - TypeError: unsupported operand type(s) for ^: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 72 passed, 3 deselected in 0.90s
operator: core/ReplaceBinaryOperator_Sub_BitXor, occurrence: 16
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -718,7 +718,7 @@
 
                 has_permission = permission_index >= required_index
 
-                duration = time.time() - start_time
+                duration = time.time() ^ start_time
                 github_api_request_duration_seconds.labels(operation="get_permission").observe(
                     duration
                 )
............................................................F
=================================== FAILURES ===================================
_____________ TestUserHasPermission.test_user_has_permission_true ______________

self = <tests.test_github_client.TestUserHasPermission object at 0x7f8aff6bcf50>

    def test_user_has_permission_true(self):
        """Test required permission satisfied returns True."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_collaborator_permission.return_value = "write"
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=1, limit=2))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.user_has_permission(123456, "owner/repo", "alice", "write")
    
>           assert result is True
E           assert False is True

tests/test_github_client.py:683: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:26:32 [warning  ] Failed to check collaborator permission for alice in owner/repo: unsupported operand type(s) for ^: 'float' and 'float' extra={'repo': 'owner/repo', 'username': 'alice', 'installation_id': 123456, 'error': "unsupported operand type(s) for ^: 'float' and 'float'"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestUserHasPermission::test_user_has_permission_true - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 60 passed, 3 deselected in 0.77s
operator: core/ReplaceBinaryOperator_Sub_BitXor, occurrence: 17
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -738,7 +738,7 @@
                 return has_permission
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() ^ start_time
                 github_api_request_duration_seconds.labels(operation="get_permission").observe(
                     duration
                 )
..............................................................F
=================================== FAILURES ===================================
_____________ TestUserHasPermission.test_user_has_permission_error _____________

self = <stampbot.github_client.GitHubAppClient object at 0x7f90e1624950>
installation_id = 123456, repo_full_name = 'owner/repo', username = 'carol'
required_permission = 'maintain'

    def user_has_permission(
        self,
        installation_id: int,
        repo_full_name: str,
        username: str,
        required_permission: str,
    ) -> bool:
        """Check whether a user has required access to a repository.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            username: GitHub username to check
            required_permission: Minimum required repository permission
    
        Returns:
            True if user meets or exceeds required permission, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.get_collaborator_permission",
            {
                "github.repo": repo_full_name,
                "github.username": username,
                "github.required_permission": required_permission,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
    
>               permission = repo.get_collaborator_permission(username)
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:710: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_collaborator_permission' id='140260233284304'>
args = ('carol',), kwargs = {}, effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestUserHasPermission object at 0x7f90e5a81a70>

    def test_user_has_permission_error(self):
        """Test permission check returns False on error."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_collaborator_permission.side_effect = Exception("API Error")
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.user_has_permission(123456, "owner/repo", "carol", "maintain")
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:754: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f90e1624950>
installation_id = 123456, repo_full_name = 'owner/repo', username = 'carol'
required_permission = 'maintain'

    def user_has_permission(
        self,
        installation_id: int,
        repo_full_name: str,
        username: str,
        required_permission: str,
    ) -> bool:
        """Check whether a user has required access to a repository.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            username: GitHub username to check
            required_permission: Minimum required repository permission
    
        Returns:
            True if user meets or exceeds required permission, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.get_collaborator_permission",
            {
                "github.repo": repo_full_name,
                "github.username": username,
                "github.required_permission": required_permission,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
    
                permission = repo.get_collaborator_permission(username)
                permission_order = ["none", "read", "triage", "write", "maintain", "admin"]
                try:
                    permission_index = permission_order.index(permission)
                    required_index = permission_order.index(required_permission)
                except ValueError:
                    permission_index = -1
                    required_index = 99
    
                has_permission = permission_index >= required_index
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_permission").observe(
                    duration
                )
                github_api_requests_total.labels(operation="get_permission", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(
                    span,
                    {
                        "github.permission": permission,
                        "github.has_permission": has_permission,
                    },
                )
                set_span_ok(span)
    
                return has_permission
    
            except Exception as e:
>               duration = time.time() ^ start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for ^: 'float' and 'float'

stampbot/github_client.py:741: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestUserHasPermission::test_user_has_permission_error - TypeError: unsupported operand type(s) for ^: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 62 passed, 3 deselected in 0.88s
operator: core/ReplaceBinaryOperator_Sub_BitXor, occurrence: 18
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -829,7 +829,7 @@
                         )
                         continue
 
-                duration = time.time() - start_time
+                duration = time.time() ^ start_time
                 github_api_request_duration_seconds.labels(operation="get_user_teams").observe(
                     duration
                 )
................................................................F
=================================== FAILURES ===================================
____________ TestGetUserTeamSlugs.test_get_user_team_slugs_success _____________

self = <tests.test_github_client.TestGetUserTeamSlugs object at 0x7fcbd35591d0>

    def test_get_user_team_slugs_success(self):
        """Test successful team membership check."""
    
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            # Mock user
            mock_user = Mock()
            mock_user.login = "alice"
    
            # Mock team that has alice as member
            mock_team = Mock()
            mock_team.has_in_members.return_value = True
    
            # Mock org
            mock_org = Mock()
            mock_org.get_team_by_slug.return_value = mock_team
    
            mock_github = Mock()
            mock_github.get_organization.return_value = mock_org
            mock_github.get_user.return_value = mock_user
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.get_user_team_slugs(
                123456, "acme", "alice", ["release-team", "deploy-team"]
            )
    
>           assert result == ["release-team", "deploy-team"]
E           AssertionError: assert [] == ['release-tea...'deploy-team']
E             
E             Right contains 2 more items, first extra item: 'release-team'
E             
E             Full diff:
E             + []
E             - [
E             -     'release-team',
E             -     'deploy-team',
E             - ]

tests/test_github_client.py:848: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 14:12:15 [debug    ] User alice is a member of team release-team extra={'org': 'acme', 'username': 'alice', 'team': 'release-team'}
2026-03-27 14:12:15 [debug    ] User alice is a member of team deploy-team extra={'org': 'acme', 'username': 'alice', 'team': 'deploy-team'}
2026-03-27 14:12:15 [warning  ] Failed to check team memberships for alice in acme: unsupported operand type(s) for ^: 'float' and 'float' extra={'org': 'acme', 'username': 'alice', 'installation_id': 123456, 'error': "unsupported operand type(s) for ^: 'float' and 'float'"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetUserTeamSlugs::test_get_user_team_slugs_success - AssertionError: assert [] == ['release-tea...'deploy-team']
  
  Right contains 2 more items, first extra item: 'release-team'
  
  Full diff:
  + []
  - [
  -     'release-team',
  -     'deploy-team',
  - ]
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 64 passed, 3 deselected in 0.79s
operator: core/ReplaceBinaryOperator_Sub_BitXor, occurrence: 19
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -849,7 +849,7 @@
                 return member_teams
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() ^ start_time
                 github_api_request_duration_seconds.labels(operation="get_user_teams").observe(
                     duration
                 )
....................................................................F
=================================== FAILURES ===================================
_______ TestGetUserTeamSlugs.test_get_user_team_slugs_general_exception ________

self = <stampbot.github_client.GitHubAppClient object at 0x7fd79550f410>
installation_id = 123456, org_name = 'acme', username = 'alice'
allowed_teams = ['release-team']

    def get_user_team_slugs(
        self,
        installation_id: int,
        org_name: str,
        username: str,
        allowed_teams: list[str],
    ) -> list[str]:
        """Get team slugs the user is a member of from the allowed teams list.
    
        Args:
            installation_id: GitHub App installation ID
            org_name: Organization name
            username: GitHub username to check
            allowed_teams: List of team slugs to check (can be "org/team" or just "team")
    
        Returns:
            List of team slugs the user is a member of
        """
        start_time = time.time()
    
        with create_span(
            "github.get_user_team_slugs",
            {
                "github.org": org_name,
                "github.username": username,
                "github.installation_id": installation_id,
                "github.teams_to_check": len(allowed_teams),
            },
        ) as span:
            member_teams: list[str] = []
    
            try:
                client = self._get_installation_client(installation_id)
>               org = client.get_organization(org_name)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:796: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_organization' id='140563901658992'>
args = ('acme',), kwargs = {}, effect = Exception('Network error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: Network error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestGetUserTeamSlugs object at 0x7fd799b905f0>

    def test_get_user_team_slugs_general_exception(self):
        """Test team membership check with general exception."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_github = Mock()
            mock_github.get_organization.side_effect = Exception("Network error")
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.get_user_team_slugs(123456, "acme", "alice", ["release-team"])
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:1010: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7fd79550f410>
installation_id = 123456, org_name = 'acme', username = 'alice'
allowed_teams = ['release-team']

    def get_user_team_slugs(
        self,
        installation_id: int,
        org_name: str,
        username: str,
        allowed_teams: list[str],
    ) -> list[str]:
        """Get team slugs the user is a member of from the allowed teams list.
    
        Args:
            installation_id: GitHub App installation ID
            org_name: Organization name
            username: GitHub username to check
            allowed_teams: List of team slugs to check (can be "org/team" or just "team")
    
        Returns:
            List of team slugs the user is a member of
        """
        start_time = time.time()
    
        with create_span(
            "github.get_user_team_slugs",
            {
                "github.org": org_name,
                "github.username": username,
                "github.installation_id": installation_id,
                "github.teams_to_check": len(allowed_teams),
            },
        ) as span:
            member_teams: list[str] = []
    
            try:
                client = self._get_installation_client(installation_id)
                org = client.get_organization(org_name)
    
                for team_ref in allowed_teams:
                    # Extract team slug (handle both "org/team" and "team" formats)
                    team_slug = team_ref.split("/")[-1] if "/" in team_ref else team_ref
    
                    try:
                        team = org.get_team_by_slug(team_slug)
                        # Check if user is a member
                        user = client.get_user(username)
                        if team.has_in_members(user):  # type: ignore[arg-type]
                            member_teams.append(team_slug)
                            logger.debug(
                                "User %s is a member of team %s",
                                username,
                                team_slug,
                                extra={
                                    "org": org_name,
                                    "username": username,
                                    "team": team_slug,
                                },
                            )
                    except GithubException as e:
                        # Team not found or no access - skip silently
                        logger.debug(
                            "Could not check team %s membership: %s",
                            team_slug,
                            e.data.get("message", str(e)) if hasattr(e, "data") else str(e),
                            extra={
                                "org": org_name,
                                "team": team_slug,
                                "username": username,
                            },
                        )
                        continue
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_user_teams").observe(
                    duration
                )
                github_api_requests_total.labels(operation="get_user_teams", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(
                    span,
                    {
                        "github.member_teams": len(member_teams),
                        "github.teams_checked": len(allowed_teams),
                    },
                )
                set_span_ok(span)
    
                return member_teams
    
            except Exception as e:
>               duration = time.time() ^ start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for ^: 'float' and 'float'

stampbot/github_client.py:852: TypeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetUserTeamSlugs::test_get_user_team_slugs_general_exception - TypeError: unsupported operand type(s) for ^: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 68 passed, 3 deselected in 0.89s
operator: core/ReplaceBinaryOperator_BitOr_Add, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -363,7 +363,7 @@
         installation_id: int,
         repo_full_name: str,
         file_path: str,
-        ref: str | None = None,
+        ref: str + None = None,
     ) -> str | None:
         """Get file content from repository.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_Add, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -364,7 +364,7 @@
         repo_full_name: str,
         file_path: str,
         ref: str | None = None,
-    ) -> str | None:
+    ) -> str + None:
         """Get file content from repository.
 
         Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_Add, occurrence: 2
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -593,7 +593,7 @@
         installation_id: int,
         repo_full_name: str,
         label_name: str,
-    ) -> bool | None:
+    ) -> bool + None:
         """Check whether a repository has a label.
 
         Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Sub, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -363,7 +363,7 @@
         installation_id: int,
         repo_full_name: str,
         file_path: str,
-        ref: str | None = None,
+        ref: str - None = None,
     ) -> str | None:
         """Get file content from repository.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.64s
operator: core/ReplaceBinaryOperator_BitOr_Sub, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -364,7 +364,7 @@
         repo_full_name: str,
         file_path: str,
         ref: str | None = None,
-    ) -> str | None:
+    ) -> str - None:
         """Get file content from repository.
 
         Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceBinaryOperator_BitOr_Sub, occurrence: 2
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -593,7 +593,7 @@
         installation_id: int,
         repo_full_name: str,
         label_name: str,
-    ) -> bool | None:
+    ) -> bool - None:
         """Check whether a repository has a label.
 
         Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/ReplaceBinaryOperator_BitOr_Mul, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -363,7 +363,7 @@
         installation_id: int,
         repo_full_name: str,
         file_path: str,
-        ref: str | None = None,
+        ref: str * None = None,
     ) -> str | None:
         """Get file content from repository.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Mul, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -364,7 +364,7 @@
         repo_full_name: str,
         file_path: str,
         ref: str | None = None,
-    ) -> str | None:
+    ) -> str * None:
         """Get file content from repository.
 
         Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/ReplaceBinaryOperator_BitOr_Mul, occurrence: 2
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -593,7 +593,7 @@
         installation_id: int,
         repo_full_name: str,
         label_name: str,
-    ) -> bool | None:
+    ) -> bool * None:
         """Check whether a repository has a label.
 
         Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Div, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -363,7 +363,7 @@
         installation_id: int,
         repo_full_name: str,
         file_path: str,
-        ref: str | None = None,
+        ref: str / None = None,
     ) -> str | None:
         """Get file content from repository.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceBinaryOperator_BitOr_Div, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -364,7 +364,7 @@
         repo_full_name: str,
         file_path: str,
         ref: str | None = None,
-    ) -> str | None:
+    ) -> str / None:
         """Get file content from repository.
 
         Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Div, occurrence: 2
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -593,7 +593,7 @@
         installation_id: int,
         repo_full_name: str,
         label_name: str,
-    ) -> bool | None:
+    ) -> bool / None:
         """Check whether a repository has a label.
 
         Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_FloorDiv, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -363,7 +363,7 @@
         installation_id: int,
         repo_full_name: str,
         file_path: str,
-        ref: str | None = None,
+        ref: str // None = None,
     ) -> str | None:
         """Get file content from repository.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.64s
operator: core/ReplaceBinaryOperator_BitOr_FloorDiv, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -364,7 +364,7 @@
         repo_full_name: str,
         file_path: str,
         ref: str | None = None,
-    ) -> str | None:
+    ) -> str // None:
         """Get file content from repository.
 
         Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_FloorDiv, occurrence: 2
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -593,7 +593,7 @@
         installation_id: int,
         repo_full_name: str,
         label_name: str,
-    ) -> bool | None:
+    ) -> bool // None:
         """Check whether a repository has a label.
 
         Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Mod, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -363,7 +363,7 @@
         installation_id: int,
         repo_full_name: str,
         file_path: str,
-        ref: str | None = None,
+        ref: str % None = None,
     ) -> str | None:
         """Get file content from repository.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_Mod, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -364,7 +364,7 @@
         repo_full_name: str,
         file_path: str,
         ref: str | None = None,
-    ) -> str | None:
+    ) -> str % None:
         """Get file content from repository.
 
         Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Mod, occurrence: 2
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -593,7 +593,7 @@
         installation_id: int,
         repo_full_name: str,
         label_name: str,
-    ) -> bool | None:
+    ) -> bool % None:
         """Check whether a repository has a label.
 
         Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_Pow, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -363,7 +363,7 @@
         installation_id: int,
         repo_full_name: str,
         file_path: str,
-        ref: str | None = None,
+        ref: str ** None = None,
     ) -> str | None:
         """Get file content from repository.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_Pow, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -364,7 +364,7 @@
         repo_full_name: str,
         file_path: str,
         ref: str | None = None,
-    ) -> str | None:
+    ) -> str ** None:
         """Get file content from repository.
 
         Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_Pow, occurrence: 2
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -593,7 +593,7 @@
         installation_id: int,
         repo_full_name: str,
         label_name: str,
-    ) -> bool | None:
+    ) -> bool ** None:
         """Check whether a repository has a label.
 
         Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_RShift, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -363,7 +363,7 @@
         installation_id: int,
         repo_full_name: str,
         file_path: str,
-        ref: str | None = None,
+        ref: str >> None = None,
     ) -> str | None:
         """Get file content from repository.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.64s
operator: core/ReplaceBinaryOperator_BitOr_RShift, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -364,7 +364,7 @@
         repo_full_name: str,
         file_path: str,
         ref: str | None = None,
-    ) -> str | None:
+    ) -> str >> None:
         """Get file content from repository.
 
         Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_RShift, occurrence: 2
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -593,7 +593,7 @@
         installation_id: int,
         repo_full_name: str,
         label_name: str,
-    ) -> bool | None:
+    ) -> bool >> None:
         """Check whether a repository has a label.
 
         Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/ReplaceBinaryOperator_BitOr_LShift, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -363,7 +363,7 @@
         installation_id: int,
         repo_full_name: str,
         file_path: str,
-        ref: str | None = None,
+        ref: str << None = None,
     ) -> str | None:
         """Get file content from repository.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_LShift, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -364,7 +364,7 @@
         repo_full_name: str,
         file_path: str,
         ref: str | None = None,
-    ) -> str | None:
+    ) -> str << None:
         """Get file content from repository.
 
         Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceBinaryOperator_BitOr_LShift, occurrence: 2
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -593,7 +593,7 @@
         installation_id: int,
         repo_full_name: str,
         label_name: str,
-    ) -> bool | None:
+    ) -> bool << None:
         """Check whether a repository has a label.
 
         Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.64s
operator: core/ReplaceBinaryOperator_BitOr_BitAnd, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -363,7 +363,7 @@
         installation_id: int,
         repo_full_name: str,
         file_path: str,
-        ref: str | None = None,
+        ref: str & None = None,
     ) -> str | None:
         """Get file content from repository.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/ReplaceBinaryOperator_BitOr_BitAnd, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -364,7 +364,7 @@
         repo_full_name: str,
         file_path: str,
         ref: str | None = None,
-    ) -> str | None:
+    ) -> str & None:
         """Get file content from repository.
 
         Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_BitAnd, occurrence: 2
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -593,7 +593,7 @@
         installation_id: int,
         repo_full_name: str,
         label_name: str,
-    ) -> bool | None:
+    ) -> bool & None:
         """Check whether a repository has a label.
 
         Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceBinaryOperator_BitOr_BitXor, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -363,7 +363,7 @@
         installation_id: int,
         repo_full_name: str,
         file_path: str,
-        ref: str | None = None,
+        ref: str ^ None = None,
     ) -> str | None:
         """Get file content from repository.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_BitXor, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -364,7 +364,7 @@
         repo_full_name: str,
         file_path: str,
         ref: str | None = None,
-    ) -> str | None:
+    ) -> str ^ None:
         """Get file content from repository.
 
         Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_BitXor, occurrence: 2
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -593,7 +593,7 @@
         installation_id: int,
         repo_full_name: str,
         label_name: str,
-    ) -> bool | None:
+    ) -> bool ^ None:
         """Check whether a repository has a label.
 
         Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceComparisonOperator_Eq_NotEq, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -470,7 +470,7 @@
                 # Find all reviews by bot that are approvals
                 bot_review_ids = []
                 for review in pr.get_reviews():
-                    if review.user.login == bot_user and review.state == "APPROVED":
+                    if review.user.login != bot_user and review.state == "APPROVED":
                         bot_review_ids.append(review.id)
 
                 duration = time.time() - start_time
..........................................................F
=================================== FAILURES ===================================
_______________ TestFindBotReviews.test_find_bot_reviews_success _______________

self = <tests.test_github_client.TestFindBotReviews object at 0x7f3560e90cd0>

    def test_find_bot_reviews_success(self):
        """Test successful bot review finding."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration.get_app.return_value = Mock(slug="stampbot")
            mock_integration_cls.return_value = mock_integration
    
            # Create mock reviews
            bot_review = Mock()
            bot_review.user.login = "stampbot[bot]"
            bot_review.state = "APPROVED"
            bot_review.id = 123
    
            other_review = Mock()
            other_review.user.login = "other-user"
            other_review.state = "APPROVED"
            other_review.id = 456
    
            mock_pr = Mock()
            mock_pr.get_reviews.return_value = [bot_review, other_review]
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.find_bot_reviews(123456, "owner/repo", 42)
    
>           assert result == [123]
E           assert [456] == [123]
E             
E             At index 0 diff: 456 != 123
E             
E             Full diff:
E               [
E             -     123,
E             +     456,
E               ]

tests/test_github_client.py:608: AssertionError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestFindBotReviews::test_find_bot_reviews_success - assert [456] == [123]
  
  At index 0 diff: 456 != 123
  
  Full diff:
    [
  -     123,
  +     456,
    ]
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 58 passed, 3 deselected in 0.76s
operator: core/ReplaceComparisonOperator_Eq_NotEq, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -470,7 +470,7 @@
                 # Find all reviews by bot that are approvals
                 bot_review_ids = []
                 for review in pr.get_reviews():
-                    if review.user.login == bot_user and review.state == "APPROVED":
+                    if review.user.login == bot_user and review.state != "APPROVED":
                         bot_review_ids.append(review.id)
 
                 duration = time.time() - start_time
..........................................................F
=================================== FAILURES ===================================
_______________ TestFindBotReviews.test_find_bot_reviews_success _______________

self = <tests.test_github_client.TestFindBotReviews object at 0x7faf3172ccd0>

    def test_find_bot_reviews_success(self):
        """Test successful bot review finding."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration.get_app.return_value = Mock(slug="stampbot")
            mock_integration_cls.return_value = mock_integration
    
            # Create mock reviews
            bot_review = Mock()
            bot_review.user.login = "stampbot[bot]"
            bot_review.state = "APPROVED"
            bot_review.id = 123
    
            other_review = Mock()
            other_review.user.login = "other-user"
            other_review.state = "APPROVED"
            other_review.id = 456
    
            mock_pr = Mock()
            mock_pr.get_reviews.return_value = [bot_review, other_review]
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.find_bot_reviews(123456, "owner/repo", 42)
    
>           assert result == [123]
E           assert [] == [123]
E             
E             Right contains one more item: 123
E             
E             Full diff:
E             + []
E             - [
E             -     123,
E             - ]

tests/test_github_client.py:608: AssertionError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestFindBotReviews::test_find_bot_reviews_success - assert [] == [123]
  
  Right contains one more item: 123
  
  Full diff:
  + []
  - [
  -     123,
  - ]
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 58 passed, 3 deselected in 0.77s
operator: core/ReplaceComparisonOperator_Eq_NotEq, occurrence: 2
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -634,7 +634,7 @@
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="failure").inc()
 
-                if e.status == 404:
+                if e.status != 404:
                     add_span_attributes(span, {"github.label_found": False})
                     set_span_ok(span)
                     return False
......................................................................F
=================================== FAILURES ===================================
________________ TestRepoHasLabel.test_repo_has_label_not_found ________________

self = <tests.test_github_client.TestRepoHasLabel object at 0x7f7aa595d590>

    def test_repo_has_label_not_found(self):
        """Test repo_has_label returns False when label is missing."""
        from github.GithubException import GithubException
    
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_label.side_effect = GithubException(404, {"message": "Not Found"}, None)
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.repo_has_label(123456, "owner/repo", "missing")
    
>           assert result is False
E           assert None is False

tests/test_github_client.py:1092: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 14:00:32 [debug    ] Could not verify label missing in owner/repo: 404 {"message": "Not Found"} extra={'repo': 'owner/repo', 'label': 'missing', 'installation_id': 123456, 'error': '404 {"message": "Not Found"}'}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestRepoHasLabel::test_repo_has_label_not_found - assert None is False
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 70 passed, 3 deselected in 0.80s
operator: core/ReplaceComparisonOperator_Eq_Lt, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -470,7 +470,7 @@
                 # Find all reviews by bot that are approvals
                 bot_review_ids = []
                 for review in pr.get_reviews():
-                    if review.user.login == bot_user and review.state == "APPROVED":
+                    if review.user.login < bot_user and review.state == "APPROVED":
                         bot_review_ids.append(review.id)
 
                 duration = time.time() - start_time
..........................................................F
=================================== FAILURES ===================================
_______________ TestFindBotReviews.test_find_bot_reviews_success _______________

self = <tests.test_github_client.TestFindBotReviews object at 0x7f488292ccd0>

    def test_find_bot_reviews_success(self):
        """Test successful bot review finding."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration.get_app.return_value = Mock(slug="stampbot")
            mock_integration_cls.return_value = mock_integration
    
            # Create mock reviews
            bot_review = Mock()
            bot_review.user.login = "stampbot[bot]"
            bot_review.state = "APPROVED"
            bot_review.id = 123
    
            other_review = Mock()
            other_review.user.login = "other-user"
            other_review.state = "APPROVED"
            other_review.id = 456
    
            mock_pr = Mock()
            mock_pr.get_reviews.return_value = [bot_review, other_review]
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.find_bot_reviews(123456, "owner/repo", 42)
    
>           assert result == [123]
E           assert [456] == [123]
E             
E             At index 0 diff: 456 != 123
E             
E             Full diff:
E               [
E             -     123,
E             +     456,
E               ]

tests/test_github_client.py:608: AssertionError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestFindBotReviews::test_find_bot_reviews_success - assert [456] == [123]
  
  At index 0 diff: 456 != 123
  
  Full diff:
    [
  -     123,
  +     456,
    ]
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 58 passed, 3 deselected in 0.76s
operator: core/ReplaceComparisonOperator_Eq_Lt, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -470,7 +470,7 @@
                 # Find all reviews by bot that are approvals
                 bot_review_ids = []
                 for review in pr.get_reviews():
-                    if review.user.login == bot_user and review.state == "APPROVED":
+                    if review.user.login == bot_user and review.state < "APPROVED":
                         bot_review_ids.append(review.id)
 
                 duration = time.time() - start_time
..........................................................F
=================================== FAILURES ===================================
_______________ TestFindBotReviews.test_find_bot_reviews_success _______________

self = <tests.test_github_client.TestFindBotReviews object at 0x7f8184184cd0>

    def test_find_bot_reviews_success(self):
        """Test successful bot review finding."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration.get_app.return_value = Mock(slug="stampbot")
            mock_integration_cls.return_value = mock_integration
    
            # Create mock reviews
            bot_review = Mock()
            bot_review.user.login = "stampbot[bot]"
            bot_review.state = "APPROVED"
            bot_review.id = 123
    
            other_review = Mock()
            other_review.user.login = "other-user"
            other_review.state = "APPROVED"
            other_review.id = 456
    
            mock_pr = Mock()
            mock_pr.get_reviews.return_value = [bot_review, other_review]
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.find_bot_reviews(123456, "owner/repo", 42)
    
>           assert result == [123]
E           assert [] == [123]
E             
E             Right contains one more item: 123
E             
E             Full diff:
E             + []
E             - [
E             -     123,
E             - ]

tests/test_github_client.py:608: AssertionError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestFindBotReviews::test_find_bot_reviews_success - assert [] == [123]
  
  Right contains one more item: 123
  
  Full diff:
  + []
  - [
  -     123,
  - ]
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 58 passed, 3 deselected in 0.75s
operator: core/ReplaceComparisonOperator_Eq_Lt, occurrence: 2
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -634,7 +634,7 @@
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="failure").inc()
 
-                if e.status == 404:
+                if e.status < 404:
                     add_span_attributes(span, {"github.label_found": False})
                     set_span_ok(span)
                     return False
......................................................................F
=================================== FAILURES ===================================
________________ TestRepoHasLabel.test_repo_has_label_not_found ________________

self = <tests.test_github_client.TestRepoHasLabel object at 0x7fe2c8799590>

    def test_repo_has_label_not_found(self):
        """Test repo_has_label returns False when label is missing."""
        from github.GithubException import GithubException
    
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_label.side_effect = GithubException(404, {"message": "Not Found"}, None)
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.repo_has_label(123456, "owner/repo", "missing")
    
>           assert result is False
E           assert None is False

tests/test_github_client.py:1092: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:29:06 [debug    ] Could not verify label missing in owner/repo: 404 {"message": "Not Found"} extra={'repo': 'owner/repo', 'label': 'missing', 'installation_id': 123456, 'error': '404 {"message": "Not Found"}'}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestRepoHasLabel::test_repo_has_label_not_found - assert None is False
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 70 passed, 3 deselected in 0.78s
operator: core/ReplaceComparisonOperator_Eq_LtE, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -470,7 +470,7 @@
                 # Find all reviews by bot that are approvals
                 bot_review_ids = []
                 for review in pr.get_reviews():
-                    if review.user.login == bot_user and review.state == "APPROVED":
+                    if review.user.login <= bot_user and review.state == "APPROVED":
                         bot_review_ids.append(review.id)
 
                 duration = time.time() - start_time
..........................................................F
=================================== FAILURES ===================================
_______________ TestFindBotReviews.test_find_bot_reviews_success _______________

self = <tests.test_github_client.TestFindBotReviews object at 0x7f9caad1ccd0>

    def test_find_bot_reviews_success(self):
        """Test successful bot review finding."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration.get_app.return_value = Mock(slug="stampbot")
            mock_integration_cls.return_value = mock_integration
    
            # Create mock reviews
            bot_review = Mock()
            bot_review.user.login = "stampbot[bot]"
            bot_review.state = "APPROVED"
            bot_review.id = 123
    
            other_review = Mock()
            other_review.user.login = "other-user"
            other_review.state = "APPROVED"
            other_review.id = 456
    
            mock_pr = Mock()
            mock_pr.get_reviews.return_value = [bot_review, other_review]
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.find_bot_reviews(123456, "owner/repo", 42)
    
>           assert result == [123]
E           assert [123, 456] == [123]
E             
E             Left contains one more item: 456
E             
E             Full diff:
E               [
E                   123,
E             +     456,
E               ]

tests/test_github_client.py:608: AssertionError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestFindBotReviews::test_find_bot_reviews_success - assert [123, 456] == [123]
  
  Left contains one more item: 456
  
  Full diff:
    [
        123,
  +     456,
    ]
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 58 passed, 3 deselected in 0.76s
operator: core/ReplaceComparisonOperator_Eq_LtE, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -470,7 +470,7 @@
                 # Find all reviews by bot that are approvals
                 bot_review_ids = []
                 for review in pr.get_reviews():
-                    if review.user.login == bot_user and review.state == "APPROVED":
+                    if review.user.login == bot_user and review.state <= "APPROVED":
                         bot_review_ids.append(review.id)
 
                 duration = time.time() - start_time
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceComparisonOperator_Eq_LtE, occurrence: 2
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -634,7 +634,7 @@
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="failure").inc()
 
-                if e.status == 404:
+                if e.status <= 404:
                     add_span_attributes(span, {"github.label_found": False})
                     set_span_ok(span)
                     return False
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceComparisonOperator_Eq_Gt, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -470,7 +470,7 @@
                 # Find all reviews by bot that are approvals
                 bot_review_ids = []
                 for review in pr.get_reviews():
-                    if review.user.login == bot_user and review.state == "APPROVED":
+                    if review.user.login > bot_user and review.state == "APPROVED":
                         bot_review_ids.append(review.id)
 
                 duration = time.time() - start_time
..........................................................F
=================================== FAILURES ===================================
_______________ TestFindBotReviews.test_find_bot_reviews_success _______________

self = <tests.test_github_client.TestFindBotReviews object at 0x7fa610a54cd0>

    def test_find_bot_reviews_success(self):
        """Test successful bot review finding."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration.get_app.return_value = Mock(slug="stampbot")
            mock_integration_cls.return_value = mock_integration
    
            # Create mock reviews
            bot_review = Mock()
            bot_review.user.login = "stampbot[bot]"
            bot_review.state = "APPROVED"
            bot_review.id = 123
    
            other_review = Mock()
            other_review.user.login = "other-user"
            other_review.state = "APPROVED"
            other_review.id = 456
    
            mock_pr = Mock()
            mock_pr.get_reviews.return_value = [bot_review, other_review]
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.find_bot_reviews(123456, "owner/repo", 42)
    
>           assert result == [123]
E           assert [] == [123]
E             
E             Right contains one more item: 123
E             
E             Full diff:
E             + []
E             - [
E             -     123,
E             - ]

tests/test_github_client.py:608: AssertionError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestFindBotReviews::test_find_bot_reviews_success - assert [] == [123]
  
  Right contains one more item: 123
  
  Full diff:
  + []
  - [
  -     123,
  - ]
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 58 passed, 3 deselected in 0.76s
operator: core/ReplaceComparisonOperator_Eq_Gt, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -470,7 +470,7 @@
                 # Find all reviews by bot that are approvals
                 bot_review_ids = []
                 for review in pr.get_reviews():
-                    if review.user.login == bot_user and review.state == "APPROVED":
+                    if review.user.login == bot_user and review.state > "APPROVED":
                         bot_review_ids.append(review.id)
 
                 duration = time.time() - start_time
..........................................................F
=================================== FAILURES ===================================
_______________ TestFindBotReviews.test_find_bot_reviews_success _______________

self = <tests.test_github_client.TestFindBotReviews object at 0x7f81458a4cd0>

    def test_find_bot_reviews_success(self):
        """Test successful bot review finding."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration.get_app.return_value = Mock(slug="stampbot")
            mock_integration_cls.return_value = mock_integration
    
            # Create mock reviews
            bot_review = Mock()
            bot_review.user.login = "stampbot[bot]"
            bot_review.state = "APPROVED"
            bot_review.id = 123
    
            other_review = Mock()
            other_review.user.login = "other-user"
            other_review.state = "APPROVED"
            other_review.id = 456
    
            mock_pr = Mock()
            mock_pr.get_reviews.return_value = [bot_review, other_review]
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.find_bot_reviews(123456, "owner/repo", 42)
    
>           assert result == [123]
E           assert [] == [123]
E             
E             Right contains one more item: 123
E             
E             Full diff:
E             + []
E             - [
E             -     123,
E             - ]

tests/test_github_client.py:608: AssertionError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestFindBotReviews::test_find_bot_reviews_success - assert [] == [123]
  
  Right contains one more item: 123
  
  Full diff:
  + []
  - [
  -     123,
  - ]
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 58 passed, 3 deselected in 0.75s
operator: core/ReplaceComparisonOperator_Eq_Gt, occurrence: 2
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -634,7 +634,7 @@
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="failure").inc()
 
-                if e.status == 404:
+                if e.status > 404:
                     add_span_attributes(span, {"github.label_found": False})
                     set_span_ok(span)
                     return False
......................................................................F
=================================== FAILURES ===================================
________________ TestRepoHasLabel.test_repo_has_label_not_found ________________

self = <tests.test_github_client.TestRepoHasLabel object at 0x7f23b6cbd590>

    def test_repo_has_label_not_found(self):
        """Test repo_has_label returns False when label is missing."""
        from github.GithubException import GithubException
    
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_label.side_effect = GithubException(404, {"message": "Not Found"}, None)
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.repo_has_label(123456, "owner/repo", "missing")
    
>           assert result is False
E           assert None is False

tests/test_github_client.py:1092: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:38:58 [debug    ] Could not verify label missing in owner/repo: 404 {"message": "Not Found"} extra={'repo': 'owner/repo', 'label': 'missing', 'installation_id': 123456, 'error': '404 {"message": "Not Found"}'}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestRepoHasLabel::test_repo_has_label_not_found - assert None is False
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 70 passed, 3 deselected in 0.78s
operator: core/ReplaceComparisonOperator_Eq_GtE, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -470,7 +470,7 @@
                 # Find all reviews by bot that are approvals
                 bot_review_ids = []
                 for review in pr.get_reviews():
-                    if review.user.login == bot_user and review.state == "APPROVED":
+                    if review.user.login >= bot_user and review.state == "APPROVED":
                         bot_review_ids.append(review.id)
 
                 duration = time.time() - start_time
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceComparisonOperator_Eq_GtE, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -470,7 +470,7 @@
                 # Find all reviews by bot that are approvals
                 bot_review_ids = []
                 for review in pr.get_reviews():
-                    if review.user.login == bot_user and review.state == "APPROVED":
+                    if review.user.login == bot_user and review.state >= "APPROVED":
                         bot_review_ids.append(review.id)
 
                 duration = time.time() - start_time
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceComparisonOperator_Eq_GtE, occurrence: 2
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -634,7 +634,7 @@
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="failure").inc()
 
-                if e.status == 404:
+                if e.status >= 404:
                     add_span_attributes(span, {"github.label_found": False})
                     set_span_ok(span)
                     return False
.......................................................................F [ 34%]

=================================== FAILURES ===================================
____________ TestRepoHasLabel.test_repo_has_label_github_exception _____________

self = <tests.test_github_client.TestRepoHasLabel object at 0x7f4213f21f30>

    def test_repo_has_label_github_exception(self):
        """Test repo_has_label returns None on GitHubException errors."""
        from github.GithubException import GithubException
    
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_label.side_effect = GithubException(500, {"message": "Error"}, None)
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.repo_has_label(123456, "owner/repo", "autoapprove")
    
>           assert result is None
E           assert False is None

tests/test_github_client.py:1130: AssertionError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestRepoHasLabel::test_repo_has_label_github_exception - assert False is None
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 71 passed, 3 deselected in 0.77s
operator: core/ReplaceComparisonOperator_Eq_Is, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -470,7 +470,7 @@
                 # Find all reviews by bot that are approvals
                 bot_review_ids = []
                 for review in pr.get_reviews():
-                    if review.user.login == bot_user and review.state == "APPROVED":
+                    if review.user.login is bot_user and review.state == "APPROVED":
                         bot_review_ids.append(review.id)
 
                 duration = time.time() - start_time
..........................................................F
=================================== FAILURES ===================================
_______________ TestFindBotReviews.test_find_bot_reviews_success _______________

self = <tests.test_github_client.TestFindBotReviews object at 0x7f5049040cd0>

    def test_find_bot_reviews_success(self):
        """Test successful bot review finding."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration.get_app.return_value = Mock(slug="stampbot")
            mock_integration_cls.return_value = mock_integration
    
            # Create mock reviews
            bot_review = Mock()
            bot_review.user.login = "stampbot[bot]"
            bot_review.state = "APPROVED"
            bot_review.id = 123
    
            other_review = Mock()
            other_review.user.login = "other-user"
            other_review.state = "APPROVED"
            other_review.id = 456
    
            mock_pr = Mock()
            mock_pr.get_reviews.return_value = [bot_review, other_review]
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.find_bot_reviews(123456, "owner/repo", 42)
    
>           assert result == [123]
E           assert [] == [123]
E             
E             Right contains one more item: 123
E             
E             Full diff:
E             + []
E             - [
E             -     123,
E             - ]

tests/test_github_client.py:608: AssertionError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestFindBotReviews::test_find_bot_reviews_success - assert [] == [123]
  
  Right contains one more item: 123
  
  Full diff:
  + []
  - [
  -     123,
  - ]
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 58 passed, 3 deselected in 0.76s
operator: core/ReplaceComparisonOperator_Eq_Is, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -470,7 +470,7 @@
                 # Find all reviews by bot that are approvals
                 bot_review_ids = []
                 for review in pr.get_reviews():
-                    if review.user.login == bot_user and review.state == "APPROVED":
+                    if review.user.login == bot_user and review.state is "APPROVED":
                         bot_review_ids.append(review.id)
 
                 duration = time.time() - start_time
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
=============================== warnings summary ===============================
tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client
  /home/runner/work/stampbot/stampbot/stampbot/github_client.py:473: SyntaxWarning: "is" with 'str' literal. Did you mean "=="?
    if review.user.login == bot_user and review.state is "APPROVED":

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
211 passed, 3 deselected, 1 warning in 1.61s
operator: core/ReplaceComparisonOperator_Eq_IsNot, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -470,7 +470,7 @@
                 # Find all reviews by bot that are approvals
                 bot_review_ids = []
                 for review in pr.get_reviews():
-                    if review.user.login == bot_user and review.state == "APPROVED":
+                    if review.user.login is not bot_user and review.state == "APPROVED":
                         bot_review_ids.append(review.id)
 
                 duration = time.time() - start_time
..........................................................F
=================================== FAILURES ===================================
_______________ TestFindBotReviews.test_find_bot_reviews_success _______________

self = <tests.test_github_client.TestFindBotReviews object at 0x7f74ed580cd0>

    def test_find_bot_reviews_success(self):
        """Test successful bot review finding."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration.get_app.return_value = Mock(slug="stampbot")
            mock_integration_cls.return_value = mock_integration
    
            # Create mock reviews
            bot_review = Mock()
            bot_review.user.login = "stampbot[bot]"
            bot_review.state = "APPROVED"
            bot_review.id = 123
    
            other_review = Mock()
            other_review.user.login = "other-user"
            other_review.state = "APPROVED"
            other_review.id = 456
    
            mock_pr = Mock()
            mock_pr.get_reviews.return_value = [bot_review, other_review]
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.find_bot_reviews(123456, "owner/repo", 42)
    
>           assert result == [123]
E           assert [123, 456] == [123]
E             
E             Left contains one more item: 456
E             
E             Full diff:
E               [
E                   123,
E             +     456,
E               ]

tests/test_github_client.py:608: AssertionError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestFindBotReviews::test_find_bot_reviews_success - assert [123, 456] == [123]
  
  Left contains one more item: 456
  
  Full diff:
    [
        123,
  +     456,
    ]
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 58 passed, 3 deselected in 0.75s
operator: core/ReplaceComparisonOperator_Eq_IsNot, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -470,7 +470,7 @@
                 # Find all reviews by bot that are approvals
                 bot_review_ids = []
                 for review in pr.get_reviews():
-                    if review.user.login == bot_user and review.state == "APPROVED":
+                    if review.user.login == bot_user and review.state is not "APPROVED":
                         bot_review_ids.append(review.id)
 
                 duration = time.time() - start_time
..........................................................F
=================================== FAILURES ===================================
_______________ TestFindBotReviews.test_find_bot_reviews_success _______________

self = <tests.test_github_client.TestFindBotReviews object at 0x7f19a1d2ccd0>

    def test_find_bot_reviews_success(self):
        """Test successful bot review finding."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration.get_app.return_value = Mock(slug="stampbot")
            mock_integration_cls.return_value = mock_integration
    
            # Create mock reviews
            bot_review = Mock()
            bot_review.user.login = "stampbot[bot]"
            bot_review.state = "APPROVED"
            bot_review.id = 123
    
            other_review = Mock()
            other_review.user.login = "other-user"
            other_review.state = "APPROVED"
            other_review.id = 456
    
            mock_pr = Mock()
            mock_pr.get_reviews.return_value = [bot_review, other_review]
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.find_bot_reviews(123456, "owner/repo", 42)
    
>           assert result == [123]
E           assert [] == [123]
E             
E             Right contains one more item: 123
E             
E             Full diff:
E             + []
E             - [
E             -     123,
E             - ]

tests/test_github_client.py:608: AssertionError
=============================== warnings summary ===============================
tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client
  /home/runner/work/stampbot/stampbot/stampbot/github_client.py:473: SyntaxWarning: "is not" with 'str' literal. Did you mean "!="?
    if review.user.login == bot_user and review.state is not "APPROVED":

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestFindBotReviews::test_find_bot_reviews_success - assert [] == [123]
  
  Right contains one more item: 123
  
  Full diff:
  + []
  - [
  -     123,
  - ]
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 58 passed, 3 deselected, 1 warning in 0.75s
operator: core/ReplaceComparisonOperator_GtE_Eq, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -716,7 +716,7 @@
                     permission_index = -1
                     required_index = 99
 
-                has_permission = permission_index >= required_index
+                has_permission = permission_index == required_index
 
                 duration = time.time() - start_time
                 github_api_request_duration_seconds.labels(operation="get_permission").observe(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceComparisonOperator_GtE_NotEq, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -716,7 +716,7 @@
                     permission_index = -1
                     required_index = 99
 
-                has_permission = permission_index >= required_index
+                has_permission = permission_index != required_index
 
                 duration = time.time() - start_time
                 github_api_request_duration_seconds.labels(operation="get_permission").observe(
............................................................F
=================================== FAILURES ===================================
_____________ TestUserHasPermission.test_user_has_permission_true ______________

self = <tests.test_github_client.TestUserHasPermission object at 0x7f68081a0f50>

    def test_user_has_permission_true(self):
        """Test required permission satisfied returns True."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_collaborator_permission.return_value = "write"
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=1, limit=2))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.user_has_permission(123456, "owner/repo", "alice", "write")
    
>           assert result is True
E           assert False is True

tests/test_github_client.py:683: AssertionError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestUserHasPermission::test_user_has_permission_true - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 60 passed, 3 deselected in 0.76s
operator: core/ReplaceComparisonOperator_GtE_Lt, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -716,7 +716,7 @@
                     permission_index = -1
                     required_index = 99
 
-                has_permission = permission_index >= required_index
+                has_permission = permission_index < required_index
 
                 duration = time.time() - start_time
                 github_api_request_duration_seconds.labels(operation="get_permission").observe(
............................................................F
=================================== FAILURES ===================================
_____________ TestUserHasPermission.test_user_has_permission_true ______________

self = <tests.test_github_client.TestUserHasPermission object at 0x7fd333e00f50>

    def test_user_has_permission_true(self):
        """Test required permission satisfied returns True."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_collaborator_permission.return_value = "write"
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=1, limit=2))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.user_has_permission(123456, "owner/repo", "alice", "write")
    
>           assert result is True
E           assert False is True

tests/test_github_client.py:683: AssertionError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestUserHasPermission::test_user_has_permission_true - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 60 passed, 3 deselected in 0.76s
operator: core/ReplaceComparisonOperator_GtE_LtE, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -716,7 +716,7 @@
                     permission_index = -1
                     required_index = 99
 
-                has_permission = permission_index >= required_index
+                has_permission = permission_index <= required_index
 
                 duration = time.time() - start_time
                 github_api_request_duration_seconds.labels(operation="get_permission").observe(
.............................................................F
=================================== FAILURES ===================================
_____________ TestUserHasPermission.test_user_has_permission_false _____________

self = <tests.test_github_client.TestUserHasPermission object at 0x7fa07707d090>

    def test_user_has_permission_false(self):
        """Test insufficient permission returns False."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_collaborator_permission.return_value = "read"
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=1, limit=2))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.user_has_permission(123456, "owner/repo", "bob", "write")
    
>           assert result is False
E           assert True is False

tests/test_github_client.py:720: AssertionError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestUserHasPermission::test_user_has_permission_false - assert True is False
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 61 passed, 3 deselected in 0.75s
operator: core/ReplaceComparisonOperator_GtE_Gt, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -716,7 +716,7 @@
                     permission_index = -1
                     required_index = 99
 
-                has_permission = permission_index >= required_index
+                has_permission = permission_index > required_index
 
                 duration = time.time() - start_time
                 github_api_request_duration_seconds.labels(operation="get_permission").observe(
............................................................F
=================================== FAILURES ===================================
_____________ TestUserHasPermission.test_user_has_permission_true ______________

self = <tests.test_github_client.TestUserHasPermission object at 0x7fc979edcf50>

    def test_user_has_permission_true(self):
        """Test required permission satisfied returns True."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_collaborator_permission.return_value = "write"
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=1, limit=2))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.user_has_permission(123456, "owner/repo", "alice", "write")
    
>           assert result is True
E           assert False is True

tests/test_github_client.py:683: AssertionError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestUserHasPermission::test_user_has_permission_true - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 60 passed, 3 deselected in 0.75s
operator: core/ReplaceComparisonOperator_GtE_Is, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -716,7 +716,7 @@
                     permission_index = -1
                     required_index = 99
 
-                has_permission = permission_index >= required_index
+                has_permission = permission_index is required_index
 
                 duration = time.time() - start_time
                 github_api_request_duration_seconds.labels(operation="get_permission").observe(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceComparisonOperator_GtE_IsNot, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -716,7 +716,7 @@
                     permission_index = -1
                     required_index = 99
 
-                has_permission = permission_index >= required_index
+                has_permission = permission_index is not required_index
 
                 duration = time.time() - start_time
                 github_api_request_duration_seconds.labels(operation="get_permission").observe(
............................................................F
=================================== FAILURES ===================================
_____________ TestUserHasPermission.test_user_has_permission_true ______________

self = <tests.test_github_client.TestUserHasPermission object at 0x7f1169b50f50>

    def test_user_has_permission_true(self):
        """Test required permission satisfied returns True."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_collaborator_permission.return_value = "write"
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=1, limit=2))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.user_has_permission(123456, "owner/repo", "alice", "write")
    
>           assert result is True
E           assert False is True

tests/test_github_client.py:683: AssertionError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestUserHasPermission::test_user_has_permission_true - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 60 passed, 3 deselected in 0.76s
operator: core/ReplaceComparisonOperator_Is_IsNot, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -67,7 +67,7 @@
 
         # is_configured() guarantees app_id is set, but check for type safety
         app_id = settings.app_id
-        if app_id is None:
+        if app_id is not None:
             raise RuntimeError("App ID not configured")
 
         private_key = self._load_private_key()
......................................F
=================================== FAILURES ===================================
____ TestEnsureInitialized.test_ensure_initialized_raises_when_app_id_none _____

self = <tests.test_github_client.TestEnsureInitialized object at 0x7f069cbf4c30>

    def test_ensure_initialized_raises_when_app_id_none(self):
        """Test that _ensure_initialized raises when app_id is None."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
        ):
            mock_settings.app_id = None
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            with pytest.raises(RuntimeError, match="App ID not configured"):
>               client._ensure_initialized()

tests/test_github_client.py:62: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:74: in _ensure_initialized
    self._auth = Auth.AppAuth(app_id, private_key)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <github.Auth.AppAuth object at 0x7f0698855be0>, app_id = None
private_key = <MagicMock name='settings.private_key' id='139666305404944'>

    def __init__(
        self,
        app_id: int | str,
        private_key: str | PrivateKeyGenerator | None = None,
        *,
        sign_func: DictSignFunction | None = None,
        jwt_expiry: int = Consts.DEFAULT_JWT_EXPIRY,
        jwt_issued_at: int = Consts.DEFAULT_JWT_ISSUED_AT,
    ):
>       assert isinstance(app_id, (int, str)), app_id
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E       AssertionError: None

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/github/Auth.py:227: AssertionError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestEnsureInitialized::test_ensure_initialized_raises_when_app_id_none - AssertionError: None
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 38 passed, 3 deselected in 0.77s
operator: core/ReplaceComparisonOperator_Is_IsNot, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -102,7 +102,7 @@
         """
         key: str | None = settings.private_key
 
-        if key is None:
+        if key is not None:
             raise RuntimeError("Private key not configured")
 
         # If it looks like PEM content, use directly
.......................................F
=================================== FAILURES ===================================
____________ TestEnsureInitialized.test_ensure_initialized_success _____________

self = <tests.test_github_client.TestEnsureInitialized object at 0x7f890230cb00>

    def test_ensure_initialized_success(self):
        """Test successful initialization."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth") as mock_auth,
            patch("stampbot.github_client.GithubIntegration") as mock_integration,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           client._ensure_initialized()

tests/test_github_client.py:78: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:73: in _ensure_initialized
    private_key = self._load_private_key()
                  ^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f88fe16bbf0>

    def _load_private_key(self) -> str:
        """Load private key from settings or file.
    
        Returns:
            Private key contents as PEM string.
    
        Raises:
            RuntimeError: If private key is not configured.
            ValueError: If private key is not valid PEM format.
            OSError: If private key file cannot be read.
        """
        key: str | None = settings.private_key
    
        if key is not None:
>           raise RuntimeError("Private key not configured")
E           RuntimeError: Private key not configured

stampbot/github_client.py:106: RuntimeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestEnsureInitialized::test_ensure_initialized_success - RuntimeError: Private key not configured
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 39 passed, 3 deselected in 0.78s
operator: core/ReplaceComparisonOperator_IsNot_Is, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -86,7 +86,7 @@
             RuntimeError: If GitHub App credentials are not configured.
         """
         self._ensure_initialized()
-        assert self._integration is not None  # guaranteed by _ensure_initialized
+        assert self._integration is None  # guaranteed by _ensure_initialized
         return self._integration
 
     def _load_private_key(self) -> str:
...............................................F
=================================== FAILURES ===================================
________ TestGetInstallationClient.test_get_installation_client_success ________

self = <tests.test_github_client.TestGetInstallationClient object at 0x7f3023dfc050>

    def test_get_installation_client_success(self):
        """Test successful installation client creation."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client._get_installation_client(123456)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:211: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:155: in _get_installation_client
    auth = self.integration.get_access_token(installation_id)
           ^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f301fa9c830>

    @property
    def integration(self) -> GithubIntegration:
        """Get GitHub integration, initializing if needed.
    
        Returns:
            Configured GithubIntegration instance.
    
        Raises:
            RuntimeError: If GitHub App credentials are not configured.
        """
        self._ensure_initialized()
>       assert self._integration is None  # guaranteed by _ensure_initialized
               ^^^^^^^^^^^^^^^^^^^^^^^^^
E       AssertionError

stampbot/github_client.py:89: AssertionError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetInstallationClient::test_get_installation_client_success - AssertionError
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 47 passed, 3 deselected in 0.77s
operator: core/ReplaceUnaryOperator_USub_UAdd, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -713,7 +713,7 @@
                     permission_index = permission_order.index(permission)
                     required_index = permission_order.index(required_permission)
                 except ValueError:
-                    permission_index = -1
+                    permission_index = +1
                     required_index = 99
 
                 has_permission = permission_index >= required_index
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceUnaryOperator_USub_UAdd, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -797,7 +797,7 @@
 
                 for team_ref in allowed_teams:
                     # Extract team slug (handle both "org/team" and "team" formats)
-                    team_slug = team_ref.split("/")[-1] if "/" in team_ref else team_ref
+                    team_slug = team_ref.split("/")[+1] if "/" in team_ref else team_ref
 
                     try:
                         team = org.get_team_by_slug(team_slug)
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.64s
operator: core/ReplaceUnaryOperator_USub_Invert, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -713,7 +713,7 @@
                     permission_index = permission_order.index(permission)
                     required_index = permission_order.index(required_permission)
                 except ValueError:
-                    permission_index = -1
+                    permission_index = ~1
                     required_index = 99
 
                 has_permission = permission_index >= required_index
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceUnaryOperator_USub_Invert, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -797,7 +797,7 @@
 
                 for team_ref in allowed_teams:
                     # Extract team slug (handle both "org/team" and "team" formats)
-                    team_slug = team_ref.split("/")[-1] if "/" in team_ref else team_ref
+                    team_slug = team_ref.split("/")[~1] if "/" in team_ref else team_ref
 
                     try:
                         team = org.get_team_by_slug(team_slug)
.................................................................F
=================================== FAILURES ===================================
________ TestGetUserTeamSlugs.test_get_user_team_slugs_with_org_prefix _________

self = <tests.test_github_client.TestGetUserTeamSlugs object at 0x7f1343921310>

    def test_get_user_team_slugs_with_org_prefix(self):
        """Test team membership check with org/team format."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_user = Mock()
            mock_team = Mock()
            mock_team.has_in_members.return_value = True
            mock_org = Mock()
            mock_org.get_team_by_slug.return_value = mock_team
    
            mock_github = Mock()
            mock_github.get_organization.return_value = mock_org
            mock_github.get_user.return_value = mock_user
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.get_user_team_slugs(123456, "acme", "alice", ["acme/release-team"])
    
            # Should extract "release-team" from "acme/release-team"
>           mock_org.get_team_by_slug.assert_called_with("release-team")

tests/test_github_client.py:892: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_organization().get_team_by_slug' id='139720641880352'>
args = ('release-team',), kwargs = {}, expected = call('release-team')
actual = call('acme')
_error_message = <function NonCallableMock.assert_called_with.<locals>._error_message at 0x7f133f33cb40>
cause = None

    def assert_called_with(self, /, *args, **kwargs):
        """assert that the last call was made with the specified arguments.
    
        Raises an AssertionError if the args and keyword args passed in are
        different to the last call to the mock."""
        if self.call_args is None:
            expected = self._format_mock_call_signature(args, kwargs)
            actual = 'not called.'
            error_message = ('expected call not found.\nExpected: %s\n  Actual: %s'
                    % (expected, actual))
            raise AssertionError(error_message)
    
        def _error_message():
            msg = self._format_mock_failure_message(args, kwargs)
            return msg
        expected = self._call_matcher(_Call((args, kwargs), two=True))
        actual = self._call_matcher(self.call_args)
        if actual != expected:
            cause = expected if isinstance(expected, Exception) else None
>           raise AssertionError(_error_message()) from cause
E           AssertionError: expected call not found.
E           Expected: get_team_by_slug('release-team')
E             Actual: get_team_by_slug('acme')

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:985: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:29:55 [debug    ] User alice is a member of team acme extra={'org': 'acme', 'username': 'alice', 'team': 'acme'}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetUserTeamSlugs::test_get_user_team_slugs_with_org_prefix - AssertionError: expected call not found.
Expected: get_team_by_slug('release-team')
  Actual: get_team_by_slug('acme')
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 65 passed, 3 deselected in 0.81s
operator: core/ReplaceUnaryOperator_USub_Not, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -713,7 +713,7 @@
                     permission_index = permission_order.index(permission)
                     required_index = permission_order.index(required_permission)
                 except ValueError:
-                    permission_index = -1
+                    permission_index = not 1
                     required_index = 99
 
                 has_permission = permission_index >= required_index
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceUnaryOperator_USub_Not, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -797,7 +797,7 @@
 
                 for team_ref in allowed_teams:
                     # Extract team slug (handle both "org/team" and "team" formats)
-                    team_slug = team_ref.split("/")[-1] if "/" in team_ref else team_ref
+                    team_slug = team_ref.split("/")[not 1] if "/" in team_ref else team_ref
 
                     try:
                         team = org.get_team_by_slug(team_slug)
.................................................................F
=================================== FAILURES ===================================
________ TestGetUserTeamSlugs.test_get_user_team_slugs_with_org_prefix _________

self = <tests.test_github_client.TestGetUserTeamSlugs object at 0x7fa6586f5310>

    def test_get_user_team_slugs_with_org_prefix(self):
        """Test team membership check with org/team format."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_user = Mock()
            mock_team = Mock()
            mock_team.has_in_members.return_value = True
            mock_org = Mock()
            mock_org.get_team_by_slug.return_value = mock_team
    
            mock_github = Mock()
            mock_github.get_organization.return_value = mock_org
            mock_github.get_user.return_value = mock_user
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.get_user_team_slugs(123456, "acme", "alice", ["acme/release-team"])
    
            # Should extract "release-team" from "acme/release-team"
>           mock_org.get_team_by_slug.assert_called_with("release-team")

tests/test_github_client.py:892: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_organization().get_team_by_slug' id='140352356540704'>
args = ('release-team',), kwargs = {}, expected = call('release-team')
actual = call('acme')
_error_message = <function NonCallableMock.assert_called_with.<locals>._error_message at 0x7fa654548b40>
cause = None

    def assert_called_with(self, /, *args, **kwargs):
        """assert that the last call was made with the specified arguments.
    
        Raises an AssertionError if the args and keyword args passed in are
        different to the last call to the mock."""
        if self.call_args is None:
            expected = self._format_mock_call_signature(args, kwargs)
            actual = 'not called.'
            error_message = ('expected call not found.\nExpected: %s\n  Actual: %s'
                    % (expected, actual))
            raise AssertionError(error_message)
    
        def _error_message():
            msg = self._format_mock_failure_message(args, kwargs)
            return msg
        expected = self._call_matcher(_Call((args, kwargs), two=True))
        actual = self._call_matcher(self.call_args)
        if actual != expected:
            cause = expected if isinstance(expected, Exception) else None
>           raise AssertionError(_error_message()) from cause
E           AssertionError: expected call not found.
E           Expected: get_team_by_slug('release-team')
E             Actual: get_team_by_slug('acme')

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:985: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 14:04:31 [debug    ] User alice is a member of team acme extra={'org': 'acme', 'username': 'alice', 'team': 'acme'}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetUserTeamSlugs::test_get_user_team_slugs_with_org_prefix - AssertionError: expected call not found.
Expected: get_team_by_slug('release-team')
  Actual: get_team_by_slug('acme')
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 65 passed, 3 deselected in 0.82s
operator: core/ReplaceUnaryOperator_Delete_USub, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -713,7 +713,7 @@
                     permission_index = permission_order.index(permission)
                     required_index = permission_order.index(required_permission)
                 except ValueError:
-                    permission_index = -1
+                    permission_index = 1
                     required_index = 99
 
                 has_permission = permission_index >= required_index
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceUnaryOperator_Delete_USub, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -797,7 +797,7 @@
 
                 for team_ref in allowed_teams:
                     # Extract team slug (handle both "org/team" and "team" formats)
-                    team_slug = team_ref.split("/")[-1] if "/" in team_ref else team_ref
+                    team_slug = team_ref.split("/")[1] if "/" in team_ref else team_ref
 
                     try:
                         team = org.get_team_by_slug(team_slug)
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceUnaryOperator_Delete_Not, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -62,7 +62,7 @@
         if self._initialized:
             return
 
-        if not is_configured():
+        if  is_configured():
             raise RuntimeError("GitHub App not configured. Visit /setup to create your GitHub App.")
 
         # is_configured() guarantees app_id is set, but check for type safety
.....................................F
=================================== FAILURES ===================================
___ TestEnsureInitialized.test_ensure_initialized_raises_when_not_configured ___

self = <tests.test_github_client.TestEnsureInitialized object at 0x7f4db6253c50>

    def test_ensure_initialized_raises_when_not_configured(self):
        """Test that _ensure_initialized raises when app not configured."""
        with patch("stampbot.github_client.is_configured", return_value=False):
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           with pytest.raises(RuntimeError, match="GitHub App not configured"):
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E           Failed: DID NOT RAISE <class 'RuntimeError'>

tests/test_github_client.py:47: Failed
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestEnsureInitialized::test_ensure_initialized_raises_when_not_configured - Failed: DID NOT RAISE <class 'RuntimeError'>
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 37 passed, 3 deselected in 0.71s
operator: core/ReplaceUnaryOperator_Delete_Not, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -117,7 +117,7 @@
 
             # Security: prevent path traversal by ensuring the path doesn't escape
             # expected directories and doesn't follow symlinks to unexpected locations
-            if ".." in str(key_path) or not os.path.isfile(key_path):
+            if ".." in str(key_path) or  os.path.isfile(key_path):
                 raise ValueError(f"Invalid private key path: {key}")
 
             try:
...........................................F
=================================== FAILURES ===================================
___________ TestLoadPrivateKey.test_load_private_key_reads_from_file ___________

self = <tests.test_github_client.TestLoadPrivateKey object at 0x7f09612156e0>
tmp_path = PosixPath('/tmp/pytest-of-runner/pytest-573/test_load_private_key_reads_fr0')

    def test_load_private_key_reads_from_file(self, tmp_path):
        """Test that private key is read from file when path provided."""
        pem_key = TEST_PEM_KEY
        key_file = tmp_path / "private-key.pem"
        key_file.write_text(pem_key)
    
        with patch("stampbot.github_client.settings") as mock_settings:
            mock_settings.private_key = str(key_file)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client._load_private_key()
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:136: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f095e09a250>

    def _load_private_key(self) -> str:
        """Load private key from settings or file.
    
        Returns:
            Private key contents as PEM string.
    
        Raises:
            RuntimeError: If private key is not configured.
            ValueError: If private key is not valid PEM format.
            OSError: If private key file cannot be read.
        """
        key: str | None = settings.private_key
    
        if key is None:
            raise RuntimeError("Private key not configured")
    
        # If it looks like PEM content, use directly
        if key.startswith("-----BEGIN"):
            pem_content = key
        else:
            # Treat as file path - read the file
            import os
            from pathlib import Path
    
            key_path = Path(key).resolve()
    
            # Security: prevent path traversal by ensuring the path doesn't escape
            # expected directories and doesn't follow symlinks to unexpected locations
            if ".." in str(key_path) or  os.path.isfile(key_path):
>               raise ValueError(f"Invalid private key path: {key}")
E               ValueError: Invalid private key path: /tmp/pytest-of-runner/pytest-573/test_load_private_key_reads_fr0/private-key.pem

stampbot/github_client.py:121: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestLoadPrivateKey::test_load_private_key_reads_from_file - ValueError: Invalid private key path: /tmp/pytest-of-runner/pytest-573/test_load_private_key_reads_fr0/private-key.pem
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 43 passed, 3 deselected in 0.76s
operator: core/ReplaceUnaryOperator_Delete_Not, occurrence: 2
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -128,7 +128,7 @@
                 raise
 
         # Validate the content is actually a PEM-formatted key
-        if not pem_content.strip().startswith("-----BEGIN"):
+        if  pem_content.strip().startswith("-----BEGIN"):
             raise ValueError("Private key must be in PEM format")
 
         return pem_content
.......................................F
=================================== FAILURES ===================================
____________ TestEnsureInitialized.test_ensure_initialized_success _____________

self = <tests.test_github_client.TestEnsureInitialized object at 0x7fd0d16b0b00>

    def test_ensure_initialized_success(self):
        """Test successful initialization."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth") as mock_auth,
            patch("stampbot.github_client.GithubIntegration") as mock_integration,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           client._ensure_initialized()

tests/test_github_client.py:78: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:73: in _ensure_initialized
    private_key = self._load_private_key()
                  ^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7fd0cd567bf0>

    def _load_private_key(self) -> str:
        """Load private key from settings or file.
    
        Returns:
            Private key contents as PEM string.
    
        Raises:
            RuntimeError: If private key is not configured.
            ValueError: If private key is not valid PEM format.
            OSError: If private key file cannot be read.
        """
        key: str | None = settings.private_key
    
        if key is None:
            raise RuntimeError("Private key not configured")
    
        # If it looks like PEM content, use directly
        if key.startswith("-----BEGIN"):
            pem_content = key
        else:
            # Treat as file path - read the file
            import os
            from pathlib import Path
    
            key_path = Path(key).resolve()
    
            # Security: prevent path traversal by ensuring the path doesn't escape
            # expected directories and doesn't follow symlinks to unexpected locations
            if ".." in str(key_path) or not os.path.isfile(key_path):
                raise ValueError(f"Invalid private key path: {key}")
    
            try:
                with open(key_path) as f:
                    pem_content = f.read()
            except Exception as e:
                logger.error("Failed to read private key from file: %s", e)
                raise
    
        # Validate the content is actually a PEM-formatted key
        if  pem_content.strip().startswith("-----BEGIN"):
>           raise ValueError("Private key must be in PEM format")
E           ValueError: Private key must be in PEM format

stampbot/github_client.py:132: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestEnsureInitialized::test_ensure_initialized_success - ValueError: Private key must be in PEM format
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 39 passed, 3 deselected in 0.76s
operator: core/AddNot, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -59,7 +59,7 @@
         Raises:
             RuntimeError: If GitHub App credentials are not configured.
         """
-        if self._initialized:
+        if not self._initialized:
             return
 
         if not is_configured():
.....................................F
=================================== FAILURES ===================================
___ TestEnsureInitialized.test_ensure_initialized_raises_when_not_configured ___

self = <tests.test_github_client.TestEnsureInitialized object at 0x7f096165fc50>

    def test_ensure_initialized_raises_when_not_configured(self):
        """Test that _ensure_initialized raises when app not configured."""
        with patch("stampbot.github_client.is_configured", return_value=False):
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           with pytest.raises(RuntimeError, match="GitHub App not configured"):
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E           Failed: DID NOT RAISE <class 'RuntimeError'>

tests/test_github_client.py:47: Failed
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestEnsureInitialized::test_ensure_initialized_raises_when_not_configured - Failed: DID NOT RAISE <class 'RuntimeError'>
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 37 passed, 3 deselected in 0.71s
operator: core/AddNot, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -62,7 +62,7 @@
         if self._initialized:
             return
 
-        if not is_configured():
+        if not not is_configured():
             raise RuntimeError("GitHub App not configured. Visit /setup to create your GitHub App.")
 
         # is_configured() guarantees app_id is set, but check for type safety
.....................................F
=================================== FAILURES ===================================
___ TestEnsureInitialized.test_ensure_initialized_raises_when_not_configured ___

self = <tests.test_github_client.TestEnsureInitialized object at 0x7f9e9a657c50>

    def test_ensure_initialized_raises_when_not_configured(self):
        """Test that _ensure_initialized raises when app not configured."""
        with patch("stampbot.github_client.is_configured", return_value=False):
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           with pytest.raises(RuntimeError, match="GitHub App not configured"):
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E           Failed: DID NOT RAISE <class 'RuntimeError'>

tests/test_github_client.py:47: Failed
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestEnsureInitialized::test_ensure_initialized_raises_when_not_configured - Failed: DID NOT RAISE <class 'RuntimeError'>
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 37 passed, 3 deselected in 0.71s
operator: core/AddNot, occurrence: 2
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -67,7 +67,7 @@
 
         # is_configured() guarantees app_id is set, but check for type safety
         app_id = settings.app_id
-        if app_id is None:
+        if not app_id is None:
             raise RuntimeError("App ID not configured")
 
         private_key = self._load_private_key()
......................................F
=================================== FAILURES ===================================
____ TestEnsureInitialized.test_ensure_initialized_raises_when_app_id_none _____

self = <tests.test_github_client.TestEnsureInitialized object at 0x7f98c89d0c30>

    def test_ensure_initialized_raises_when_app_id_none(self):
        """Test that _ensure_initialized raises when app_id is None."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
        ):
            mock_settings.app_id = None
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            with pytest.raises(RuntimeError, match="App ID not configured"):
>               client._ensure_initialized()

tests/test_github_client.py:62: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:74: in _ensure_initialized
    self._auth = Auth.AppAuth(app_id, private_key)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <github.Auth.AppAuth object at 0x7f98c4655be0>, app_id = None
private_key = <MagicMock name='settings.private_key' id='140294106730512'>

    def __init__(
        self,
        app_id: int | str,
        private_key: str | PrivateKeyGenerator | None = None,
        *,
        sign_func: DictSignFunction | None = None,
        jwt_expiry: int = Consts.DEFAULT_JWT_EXPIRY,
        jwt_issued_at: int = Consts.DEFAULT_JWT_ISSUED_AT,
    ):
>       assert isinstance(app_id, (int, str)), app_id
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E       AssertionError: None

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/github/Auth.py:227: AssertionError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestEnsureInitialized::test_ensure_initialized_raises_when_app_id_none - AssertionError: None
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 38 passed, 3 deselected in 0.76s
operator: core/AddNot, occurrence: 3
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -86,7 +86,7 @@
             RuntimeError: If GitHub App credentials are not configured.
         """
         self._ensure_initialized()
-        assert self._integration is not None  # guaranteed by _ensure_initialized
+        assert not self._integration is not None  # guaranteed by _ensure_initialized
         return self._integration
 
     def _load_private_key(self) -> str:
...............................................F
=================================== FAILURES ===================================
________ TestGetInstallationClient.test_get_installation_client_success ________

self = <tests.test_github_client.TestGetInstallationClient object at 0x7f88a1d0c050>

    def test_get_installation_client_success(self):
        """Test successful installation client creation."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client._get_installation_client(123456)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:211: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:155: in _get_installation_client
    auth = self.integration.get_access_token(installation_id)
           ^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f889dd84830>

    @property
    def integration(self) -> GithubIntegration:
        """Get GitHub integration, initializing if needed.
    
        Returns:
            Configured GithubIntegration instance.
    
        Raises:
            RuntimeError: If GitHub App credentials are not configured.
        """
        self._ensure_initialized()
>       assert not self._integration is not None  # guaranteed by _ensure_initialized
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E       AssertionError

stampbot/github_client.py:89: AssertionError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetInstallationClient::test_get_installation_client_success - AssertionError
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 47 passed, 3 deselected in 0.78s
operator: core/AddNot, occurrence: 4
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -102,7 +102,7 @@
         """
         key: str | None = settings.private_key
 
-        if key is None:
+        if not key is None:
             raise RuntimeError("Private key not configured")
 
         # If it looks like PEM content, use directly
.......................................F
=================================== FAILURES ===================================
____________ TestEnsureInitialized.test_ensure_initialized_success _____________

self = <tests.test_github_client.TestEnsureInitialized object at 0x7f2206b1cb00>

    def test_ensure_initialized_success(self):
        """Test successful initialization."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth") as mock_auth,
            patch("stampbot.github_client.GithubIntegration") as mock_integration,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           client._ensure_initialized()

tests/test_github_client.py:78: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:73: in _ensure_initialized
    private_key = self._load_private_key()
                  ^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f2203983bf0>

    def _load_private_key(self) -> str:
        """Load private key from settings or file.
    
        Returns:
            Private key contents as PEM string.
    
        Raises:
            RuntimeError: If private key is not configured.
            ValueError: If private key is not valid PEM format.
            OSError: If private key file cannot be read.
        """
        key: str | None = settings.private_key
    
        if not key is None:
>           raise RuntimeError("Private key not configured")
E           RuntimeError: Private key not configured

stampbot/github_client.py:106: RuntimeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestEnsureInitialized::test_ensure_initialized_success - RuntimeError: Private key not configured
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 39 passed, 3 deselected in 0.76s
operator: core/AddNot, occurrence: 5
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -106,7 +106,7 @@
             raise RuntimeError("Private key not configured")
 
         # If it looks like PEM content, use directly
-        if key.startswith("-----BEGIN"):
+        if not key.startswith("-----BEGIN"):
             pem_content = key
         else:
             # Treat as file path - read the file
.......................................F
=================================== FAILURES ===================================
____________ TestEnsureInitialized.test_ensure_initialized_success _____________

self = <tests.test_github_client.TestEnsureInitialized object at 0x7f87abe74b00>

    def test_ensure_initialized_success(self):
        """Test successful initialization."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth") as mock_auth,
            patch("stampbot.github_client.GithubIntegration") as mock_integration,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           client._ensure_initialized()

tests/test_github_client.py:78: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:73: in _ensure_initialized
    private_key = self._load_private_key()
                  ^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f87a8e93bf0>

    def _load_private_key(self) -> str:
        """Load private key from settings or file.
    
        Returns:
            Private key contents as PEM string.
    
        Raises:
            RuntimeError: If private key is not configured.
            ValueError: If private key is not valid PEM format.
            OSError: If private key file cannot be read.
        """
        key: str | None = settings.private_key
    
        if key is None:
            raise RuntimeError("Private key not configured")
    
        # If it looks like PEM content, use directly
        if not key.startswith("-----BEGIN"):
            pem_content = key
        else:
            # Treat as file path - read the file
            import os
            from pathlib import Path
    
            key_path = Path(key).resolve()
    
            # Security: prevent path traversal by ensuring the path doesn't escape
            # expected directories and doesn't follow symlinks to unexpected locations
            if ".." in str(key_path) or not os.path.isfile(key_path):
>               raise ValueError(f"Invalid private key path: {key}")
E               ValueError: Invalid private key path: -----BEGIN RSA PRIVATE KEY-----
E               test
E               -----END RSA PRIVATE KEY-----

stampbot/github_client.py:121: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestEnsureInitialized::test_ensure_initialized_success - ValueError: Invalid private key path: -----BEGIN RSA PRIVATE KEY-----
test
-----END RSA PRIVATE KEY-----
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 39 passed, 3 deselected in 0.76s
operator: core/AddNot, occurrence: 6
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -117,7 +117,7 @@
 
             # Security: prevent path traversal by ensuring the path doesn't escape
             # expected directories and doesn't follow symlinks to unexpected locations
-            if ".." in str(key_path) or not os.path.isfile(key_path):
+            if not ".." in str(key_path) or not os.path.isfile(key_path):
                 raise ValueError(f"Invalid private key path: {key}")
 
             try:
...........................................F
=================================== FAILURES ===================================
___________ TestLoadPrivateKey.test_load_private_key_reads_from_file ___________

self = <tests.test_github_client.TestLoadPrivateKey object at 0x7fe5848216e0>
tmp_path = PosixPath('/tmp/pytest-of-runner/pytest-616/test_load_private_key_reads_fr0')

    def test_load_private_key_reads_from_file(self, tmp_path):
        """Test that private key is read from file when path provided."""
        pem_key = TEST_PEM_KEY
        key_file = tmp_path / "private-key.pem"
        key_file.write_text(pem_key)
    
        with patch("stampbot.github_client.settings") as mock_settings:
            mock_settings.private_key = str(key_file)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client._load_private_key()
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:136: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7fe58168e250>

    def _load_private_key(self) -> str:
        """Load private key from settings or file.
    
        Returns:
            Private key contents as PEM string.
    
        Raises:
            RuntimeError: If private key is not configured.
            ValueError: If private key is not valid PEM format.
            OSError: If private key file cannot be read.
        """
        key: str | None = settings.private_key
    
        if key is None:
            raise RuntimeError("Private key not configured")
    
        # If it looks like PEM content, use directly
        if key.startswith("-----BEGIN"):
            pem_content = key
        else:
            # Treat as file path - read the file
            import os
            from pathlib import Path
    
            key_path = Path(key).resolve()
    
            # Security: prevent path traversal by ensuring the path doesn't escape
            # expected directories and doesn't follow symlinks to unexpected locations
            if not ".." in str(key_path) or not os.path.isfile(key_path):
>               raise ValueError(f"Invalid private key path: {key}")
E               ValueError: Invalid private key path: /tmp/pytest-of-runner/pytest-616/test_load_private_key_reads_fr0/private-key.pem

stampbot/github_client.py:121: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestLoadPrivateKey::test_load_private_key_reads_from_file - ValueError: Invalid private key path: /tmp/pytest-of-runner/pytest-616/test_load_private_key_reads_fr0/private-key.pem
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 43 passed, 3 deselected in 0.76s
operator: core/AddNot, occurrence: 7
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -128,7 +128,7 @@
                 raise
 
         # Validate the content is actually a PEM-formatted key
-        if not pem_content.strip().startswith("-----BEGIN"):
+        if not not pem_content.strip().startswith("-----BEGIN"):
             raise ValueError("Private key must be in PEM format")
 
         return pem_content
.......................................F
=================================== FAILURES ===================================
____________ TestEnsureInitialized.test_ensure_initialized_success _____________

self = <tests.test_github_client.TestEnsureInitialized object at 0x7f08ddfd4b00>

    def test_ensure_initialized_success(self):
        """Test successful initialization."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth") as mock_auth,
            patch("stampbot.github_client.GithubIntegration") as mock_integration,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           client._ensure_initialized()

tests/test_github_client.py:78: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:73: in _ensure_initialized
    private_key = self._load_private_key()
                  ^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f08db077bf0>

    def _load_private_key(self) -> str:
        """Load private key from settings or file.
    
        Returns:
            Private key contents as PEM string.
    
        Raises:
            RuntimeError: If private key is not configured.
            ValueError: If private key is not valid PEM format.
            OSError: If private key file cannot be read.
        """
        key: str | None = settings.private_key
    
        if key is None:
            raise RuntimeError("Private key not configured")
    
        # If it looks like PEM content, use directly
        if key.startswith("-----BEGIN"):
            pem_content = key
        else:
            # Treat as file path - read the file
            import os
            from pathlib import Path
    
            key_path = Path(key).resolve()
    
            # Security: prevent path traversal by ensuring the path doesn't escape
            # expected directories and doesn't follow symlinks to unexpected locations
            if ".." in str(key_path) or not os.path.isfile(key_path):
                raise ValueError(f"Invalid private key path: {key}")
    
            try:
                with open(key_path) as f:
                    pem_content = f.read()
            except Exception as e:
                logger.error("Failed to read private key from file: %s", e)
                raise
    
        # Validate the content is actually a PEM-formatted key
        if not not pem_content.strip().startswith("-----BEGIN"):
>           raise ValueError("Private key must be in PEM format")
E           ValueError: Private key must be in PEM format

stampbot/github_client.py:132: ValueError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestEnsureInitialized::test_ensure_initialized_success - ValueError: Private key must be in PEM format
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 39 passed, 3 deselected in 0.76s
operator: core/AddNot, occurrence: 8
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -392,7 +392,7 @@
                 repo = client.get_repo(repo_full_name)
 
                 content = repo.get_contents(file_path, ref=ref)  # type: ignore[arg-type]
-                if isinstance(content, list):
+                if not isinstance(content, list):
                     duration = time.time() - start_time
                     github_api_request_duration_seconds.labels(operation="get_file").observe(
                         duration
.......................................................F
=================================== FAILURES ===================================
__________________ TestGetRepoFile.test_get_repo_file_success __________________

self = <tests.test_github_client.TestGetRepoFile object at 0x7f0896c3ca50>

    def test_get_repo_file_success(self):
        """Test successful file retrieval."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_content = Mock()
            mock_content.decoded_content = b"file content"
            mock_repo = Mock()
            mock_repo.get_contents.return_value = mock_content
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.get_repo_file(123456, "owner/repo", "stampbot.toml")
    
>           assert result == "file content"
E           AssertionError: assert None == 'file content'

tests/test_github_client.py:480: AssertionError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetRepoFile::test_get_repo_file_success - AssertionError: assert None == 'file content'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 55 passed, 3 deselected in 0.75s
operator: core/AddNot, occurrence: 9
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -470,7 +470,7 @@
                 # Find all reviews by bot that are approvals
                 bot_review_ids = []
                 for review in pr.get_reviews():
-                    if review.user.login == bot_user and review.state == "APPROVED":
+                    if not review.user.login == bot_user and review.state == "APPROVED":
                         bot_review_ids.append(review.id)
 
                 duration = time.time() - start_time
..........................................................F
=================================== FAILURES ===================================
_______________ TestFindBotReviews.test_find_bot_reviews_success _______________

self = <tests.test_github_client.TestFindBotReviews object at 0x7fc210180cd0>

    def test_find_bot_reviews_success(self):
        """Test successful bot review finding."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration.get_app.return_value = Mock(slug="stampbot")
            mock_integration_cls.return_value = mock_integration
    
            # Create mock reviews
            bot_review = Mock()
            bot_review.user.login = "stampbot[bot]"
            bot_review.state = "APPROVED"
            bot_review.id = 123
    
            other_review = Mock()
            other_review.user.login = "other-user"
            other_review.state = "APPROVED"
            other_review.id = 456
    
            mock_pr = Mock()
            mock_pr.get_reviews.return_value = [bot_review, other_review]
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.find_bot_reviews(123456, "owner/repo", 42)
    
>           assert result == [123]
E           assert [456] == [123]
E             
E             At index 0 diff: 456 != 123
E             
E             Full diff:
E               [
E             -     123,
E             +     456,
E               ]

tests/test_github_client.py:608: AssertionError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestFindBotReviews::test_find_bot_reviews_success - assert [456] == [123]
  
  At index 0 diff: 456 != 123
  
  Full diff:
    [
  -     123,
  +     456,
    ]
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 58 passed, 3 deselected in 0.76s
operator: core/AddNot, occurrence: 10
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -634,7 +634,7 @@
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="failure").inc()
 
-                if e.status == 404:
+                if not e.status == 404:
                     add_span_attributes(span, {"github.label_found": False})
                     set_span_ok(span)
                     return False
......................................................................F
=================================== FAILURES ===================================
________________ TestRepoHasLabel.test_repo_has_label_not_found ________________

self = <tests.test_github_client.TestRepoHasLabel object at 0x7f0ba05d5590>

    def test_repo_has_label_not_found(self):
        """Test repo_has_label returns False when label is missing."""
        from github.GithubException import GithubException
    
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_label.side_effect = GithubException(404, {"message": "Not Found"}, None)
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.repo_has_label(123456, "owner/repo", "missing")
    
>           assert result is False
E           assert None is False

tests/test_github_client.py:1092: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 14:07:10 [debug    ] Could not verify label missing in owner/repo: 404 {"message": "Not Found"} extra={'repo': 'owner/repo', 'label': 'missing', 'installation_id': 123456, 'error': '404 {"message": "Not Found"}'}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestRepoHasLabel::test_repo_has_label_not_found - assert None is False
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 70 passed, 3 deselected in 0.79s
operator: core/AddNot, occurrence: 11
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -797,7 +797,7 @@
 
                 for team_ref in allowed_teams:
                     # Extract team slug (handle both "org/team" and "team" formats)
-                    team_slug = team_ref.split("/")[-1] if "/" in team_ref else team_ref
+                    team_slug = team_ref.split("/")[-1] if not "/" in team_ref else team_ref
 
                     try:
                         team = org.get_team_by_slug(team_slug)
.................................................................F
=================================== FAILURES ===================================
________ TestGetUserTeamSlugs.test_get_user_team_slugs_with_org_prefix _________

self = <tests.test_github_client.TestGetUserTeamSlugs object at 0x7f0071419310>

    def test_get_user_team_slugs_with_org_prefix(self):
        """Test team membership check with org/team format."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_user = Mock()
            mock_team = Mock()
            mock_team.has_in_members.return_value = True
            mock_org = Mock()
            mock_org.get_team_by_slug.return_value = mock_team
    
            mock_github = Mock()
            mock_github.get_organization.return_value = mock_org
            mock_github.get_user.return_value = mock_user
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.get_user_team_slugs(123456, "acme", "alice", ["acme/release-team"])
    
            # Should extract "release-team" from "acme/release-team"
>           mock_org.get_team_by_slug.assert_called_with("release-team")

tests/test_github_client.py:892: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_organization().get_team_by_slug' id='139639803814176'>
args = ('release-team',), kwargs = {}, expected = call('release-team')
actual = call('acme/release-team')
_error_message = <function NonCallableMock.assert_called_with.<locals>._error_message at 0x7f006ce0cb40>
cause = None

    def assert_called_with(self, /, *args, **kwargs):
        """assert that the last call was made with the specified arguments.
    
        Raises an AssertionError if the args and keyword args passed in are
        different to the last call to the mock."""
        if self.call_args is None:
            expected = self._format_mock_call_signature(args, kwargs)
            actual = 'not called.'
            error_message = ('expected call not found.\nExpected: %s\n  Actual: %s'
                    % (expected, actual))
            raise AssertionError(error_message)
    
        def _error_message():
            msg = self._format_mock_failure_message(args, kwargs)
            return msg
        expected = self._call_matcher(_Call((args, kwargs), two=True))
        actual = self._call_matcher(self.call_args)
        if actual != expected:
            cause = expected if isinstance(expected, Exception) else None
>           raise AssertionError(_error_message()) from cause
E           AssertionError: expected call not found.
E           Expected: get_team_by_slug('release-team')
E             Actual: get_team_by_slug('acme/release-team')

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:985: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 14:07:24 [debug    ] User alice is a member of team acme/release-team extra={'org': 'acme', 'username': 'alice', 'team': 'acme/release-team'}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetUserTeamSlugs::test_get_user_team_slugs_with_org_prefix - AssertionError: expected call not found.
Expected: get_team_by_slug('release-team')
  Actual: get_team_by_slug('acme/release-team')
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 65 passed, 3 deselected in 0.81s
operator: core/AddNot, occurrence: 12
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -803,7 +803,7 @@
                         team = org.get_team_by_slug(team_slug)
                         # Check if user is a member
                         user = client.get_user(username)
-                        if team.has_in_members(user):  # type: ignore[arg-type]
+                        if not team.has_in_members(user):  # type: ignore[arg-type]
                             member_teams.append(team_slug)
                             logger.debug(
                                 "User %s is a member of team %s",
................................................................F
=================================== FAILURES ===================================
____________ TestGetUserTeamSlugs.test_get_user_team_slugs_success _____________

self = <tests.test_github_client.TestGetUserTeamSlugs object at 0x7fae098011d0>

    def test_get_user_team_slugs_success(self):
        """Test successful team membership check."""
    
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            # Mock user
            mock_user = Mock()
            mock_user.login = "alice"
    
            # Mock team that has alice as member
            mock_team = Mock()
            mock_team.has_in_members.return_value = True
    
            # Mock org
            mock_org = Mock()
            mock_org.get_team_by_slug.return_value = mock_team
    
            mock_github = Mock()
            mock_github.get_organization.return_value = mock_org
            mock_github.get_user.return_value = mock_user
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.get_user_team_slugs(
                123456, "acme", "alice", ["release-team", "deploy-team"]
            )
    
>           assert result == ["release-team", "deploy-team"]
E           AssertionError: assert [] == ['release-tea...'deploy-team']
E             
E             Right contains 2 more items, first extra item: 'release-team'
E             
E             Full diff:
E             + []
E             - [
E             -     'release-team',
E             -     'deploy-team',
E             - ]

tests/test_github_client.py:848: AssertionError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetUserTeamSlugs::test_get_user_team_slugs_success - AssertionError: assert [] == ['release-tea...'deploy-team']
  
  Right contains 2 more items, first extra item: 'release-team'
  
  Full diff:
  + []
  - [
  -     'release-team',
  -     'deploy-team',
  - ]
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 64 passed, 3 deselected in 0.76s
operator: core/AddNot, occurrence: 13
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -820,7 +820,7 @@
                         logger.debug(
                             "Could not check team %s membership: %s",
                             team_slug,
-                            e.data.get("message", str(e)) if hasattr(e, "data") else str(e),
+                            e.data.get("message", str(e)) if not hasattr(e, "data") else str(e),
                             extra={
                                 "org": org_name,
                                 "team": team_slug,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceTrueWithFalse, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -73,7 +73,7 @@
         private_key = self._load_private_key()
         self._auth = Auth.AppAuth(app_id, private_key)
         self._integration = GithubIntegration(auth=self._auth)
-        self._initialized = True
+        self._initialized = False
 
     @property
     def integration(self) -> GithubIntegration:
.......................................F
=================================== FAILURES ===================================
____________ TestEnsureInitialized.test_ensure_initialized_success _____________

self = <tests.test_github_client.TestEnsureInitialized object at 0x7f4bb80f8b00>

    def test_ensure_initialized_success(self):
        """Test successful initialization."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth") as mock_auth,
            patch("stampbot.github_client.GithubIntegration") as mock_integration,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            client._ensure_initialized()
    
>           assert client._initialized is True
E           assert False is True
E            +  where False = <stampbot.github_client.GitHubAppClient object at 0x7f4bb4f83bf0>._initialized

tests/test_github_client.py:80: AssertionError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestEnsureInitialized::test_ensure_initialized_success - assert False is True
 +  where False = <stampbot.github_client.GitHubAppClient object at 0x7f4bb4f83bf0>._initialized
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 39 passed, 3 deselected in 0.73s
operator: core/ReplaceTrueWithFalse, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -257,7 +257,7 @@
                         "installation_id": installation_id,
                     },
                 )
-                return True
+                return False
 
             except Exception as e:
                 duration = time.time() - start_time
...................................................F
=================================== FAILURES ===================================
____________________ TestApprovePR.test_approve_pr_success _____________________

self = <tests.test_github_client.TestApprovePR object at 0x7ff14c214550>

    def test_approve_pr_success(self):
        """Test successful PR approval."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_pr = Mock()
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.approve_pr(123456, "owner/repo", 42)
    
>           assert result is True
E           assert False is True

tests/test_github_client.py:313: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:37:29 [info     ] Approved PR #42 in owner/repo  extra={'repo': 'owner/repo', 'pr_number': 42, 'installation_id': 123456}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestApprovePR::test_approve_pr_success - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 51 passed, 3 deselected in 0.73s
operator: core/ReplaceTrueWithFalse, occurrence: 2
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -335,7 +335,7 @@
                         "review_id": review_id,
                     },
                 )
-                return True
+                return False
 
             except Exception as e:
                 duration = time.time() - start_time
.....................................................F
=================================== FAILURES ===================================
______________ TestDismissApproval.test_dismiss_approval_success _______________

self = <tests.test_github_client.TestDismissApproval object at 0x7f0872b847d0>

    def test_dismiss_approval_success(self):
        """Test successful approval dismissal."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_review = Mock()
            mock_pr = Mock()
            mock_pr.get_review.return_value = mock_review
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.dismiss_approval(123456, "owner/repo", 42, 999)
    
>           assert result is True
E           assert False is True

tests/test_github_client.py:396: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:48:41 [info     ] Dismissed approval for PR #42 in owner/repo extra={'repo': 'owner/repo', 'pr_number': 42, 'installation_id': 123456, 'review_id': 999}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestDismissApproval::test_dismiss_approval_success - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 53 passed, 3 deselected in 0.73s
operator: core/ReplaceTrueWithFalse, occurrence: 3
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -565,7 +565,7 @@
                         "installation_id": installation_id,
                     },
                 )
-                return True
+                return False
 
             except Exception as e:
                 duration = time.time() - start_time
........................................................................ [ 34%]
.F
=================================== FAILURES ===================================
_______ TestCreatePrReviewComment.test_create_pr_review_comment_success ________

self = <tests.test_github_client.TestCreatePrReviewComment object at 0x7fe3260e96d0>

    def test_create_pr_review_comment_success(self):
        """Test successful review comment creation."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_pr = Mock()
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=1, limit=2))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.create_pr_review_comment(
                123456,
                "owner/repo",
                42,
                "Config error",
            )
    
>           assert result is True
E           assert False is True

tests/test_github_client.py:1213: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 14:04:47 [info     ] Posted config error review on PR #42 in owner/repo extra={'repo': 'owner/repo', 'pr_number': 42, 'installation_id': 123456}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestCreatePrReviewComment::test_create_pr_review_comment_success - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 73 passed, 3 deselected in 0.78s
operator: core/ReplaceTrueWithFalse, occurrence: 4
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -625,7 +625,7 @@
 
                 self._update_rate_limit_metrics(client, installation_id)
 
-                add_span_attributes(span, {"github.label_found": True})
+                add_span_attributes(span, {"github.label_found": False})
                 set_span_ok(span)
                 return True
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceTrueWithFalse, occurrence: 5
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -627,7 +627,7 @@
 
                 add_span_attributes(span, {"github.label_found": True})
                 set_span_ok(span)
-                return True
+                return False
 
             except GithubException as e:
                 duration = time.time() - start_time
.....................................................................F
=================================== FAILURES ===================================
__________________ TestRepoHasLabel.test_repo_has_label_true ___________________

self = <tests.test_github_client.TestRepoHasLabel object at 0x7f7822fd5450>

    def test_repo_has_label_true(self):
        """Test repo_has_label returns True when label exists."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_label.return_value = Mock()
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=1, limit=2))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.repo_has_label(123456, "owner/repo", "autoapprove")
    
>           assert result is True
E           assert False is True

tests/test_github_client.py:1054: AssertionError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestRepoHasLabel::test_repo_has_label_true - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 69 passed, 3 deselected in 0.78s
operator: core/ReplaceFalseWithTrue, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -51,7 +51,7 @@
         """Initialize GitHub App client (lazy initialization)."""
         self._auth: Auth.AppAuth | None = None
         self._integration: GithubIntegration | None = None
-        self._initialized = False
+        self._initialized = True
 
     def _ensure_initialized(self) -> None:
         """Ensure client is initialized with credentials.
...................................F
=================================== FAILURES ===================================
________ TestGitHubAppClientInit.test_init_creates_uninitialized_client ________

self = <tests.test_github_client.TestGitHubAppClientInit object at 0x7ff30afef9d0>

    def test_init_creates_uninitialized_client(self):
        """Test that __init__ creates an uninitialized client."""
        from stampbot.github_client import GitHubAppClient
    
        client = GitHubAppClient()
        assert client._auth is None
        assert client._integration is None
>       assert client._initialized is False
E       assert True is False
E        +  where True = <stampbot.github_client.GitHubAppClient object at 0x7ff306e47250>._initialized

tests/test_github_client.py:24: AssertionError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGitHubAppClientInit::test_init_creates_uninitialized_client - assert True is False
 +  where True = <stampbot.github_client.GitHubAppClient object at 0x7ff306e47250>._initialized
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.72s
operator: core/ReplaceFalseWithTrue, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -275,7 +275,7 @@
                         "error": _sanitize_error(e),
                     },
                 )
-                return False
+                return True
 
     def dismiss_approval(
         self,
....................................................F
=================================== FAILURES ===================================
____________________ TestApprovePR.test_approve_pr_failure _____________________

self = <tests.test_github_client.TestApprovePR object at 0x7f80f8388690>

    def test_approve_pr_failure(self):
        """Test PR approval failure."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_pr = Mock()
            mock_pr.create_review.side_effect = Exception("API Error")
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.approve_pr(123456, "owner/repo", 42)
    
>           assert result is False
E           assert True is False

tests/test_github_client.py:352: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 14:03:08 [error    ] Failed to approve PR #42 in owner/repo: API Error extra={'repo': 'owner/repo', 'pr_number': 42, 'installation_id': 123456, 'error': 'API Error'}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestApprovePR::test_approve_pr_failure - assert True is False
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 52 passed, 3 deselected in 0.74s
operator: core/ReplaceFalseWithTrue, occurrence: 2
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -356,7 +356,7 @@
                         "error": _sanitize_error(e),
                     },
                 )
-                return False
+                return True
 
     def get_repo_file(
         self,
......................................................F
=================================== FAILURES ===================================
______________ TestDismissApproval.test_dismiss_approval_failure _______________

self = <tests.test_github_client.TestDismissApproval object at 0x7fa96ba70910>

    def test_dismiss_approval_failure(self):
        """Test approval dismissal failure."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_review = Mock()
            mock_review.dismiss.side_effect = Exception("API Error")
            mock_pr = Mock()
            mock_pr.get_review.return_value = mock_review
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.dismiss_approval(123456, "owner/repo", 42, 999)
    
>           assert result is False
E           assert True is False

tests/test_github_client.py:437: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:49:58 [error    ] Failed to dismiss approval for PR #42 in owner/repo: API Error extra={'repo': 'owner/repo', 'pr_number': 42, 'installation_id': 123456, 'error': 'API Error'}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestDismissApproval::test_dismiss_approval_failure - assert True is False
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 54 passed, 3 deselected in 0.74s
operator: core/ReplaceFalseWithTrue, occurrence: 3
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -586,7 +586,7 @@
                         "error": _sanitize_error(e),
                     },
                 )
-                return False
+                return True
 
     def repo_has_label(
         self,
........................................................................ [ 34%]
..F
=================================== FAILURES ===================================
_______ TestCreatePrReviewComment.test_create_pr_review_comment_failure ________

self = <tests.test_github_client.TestCreatePrReviewComment object at 0x7f28b8661810>

    def test_create_pr_review_comment_failure(self):
        """Test review comment creation handles errors."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_pull.side_effect = Exception("API Error")
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.create_pr_review_comment(
                123456,
                "owner/repo",
                42,
                "Config error",
            )
    
>           assert result is False
E           assert True is False

tests/test_github_client.py:1255: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:27:12 [warning  ] Failed to post config error review for PR #42 in owner/repo: API Error extra={'repo': 'owner/repo', 'pr_number': 42, 'installation_id': 123456, 'error': 'API Error'}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestCreatePrReviewComment::test_create_pr_review_comment_failure - assert True is False
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 74 passed, 3 deselected in 0.79s
operator: core/ReplaceFalseWithTrue, occurrence: 4
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -635,7 +635,7 @@
                 github_api_requests_total.labels(operation="get_label", status="failure").inc()
 
                 if e.status == 404:
-                    add_span_attributes(span, {"github.label_found": False})
+                    add_span_attributes(span, {"github.label_found": True})
                     set_span_ok(span)
                     return False
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceFalseWithTrue, occurrence: 5
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -637,7 +637,7 @@
                 if e.status == 404:
                     add_span_attributes(span, {"github.label_found": False})
                     set_span_ok(span)
-                    return False
+                    return True
 
                 set_span_error(span, e)
                 logger.debug(
......................................................................F
=================================== FAILURES ===================================
________________ TestRepoHasLabel.test_repo_has_label_not_found ________________

self = <tests.test_github_client.TestRepoHasLabel object at 0x7f2349b55590>

    def test_repo_has_label_not_found(self):
        """Test repo_has_label returns False when label is missing."""
        from github.GithubException import GithubException
    
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_label.side_effect = GithubException(404, {"message": "Not Found"}, None)
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.repo_has_label(123456, "owner/repo", "missing")
    
>           assert result is False
E           assert True is False

tests/test_github_client.py:1092: AssertionError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestRepoHasLabel::test_repo_has_label_not_found - assert True is False
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 70 passed, 3 deselected in 0.77s
operator: core/ReplaceFalseWithTrue, occurrence: 6
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -758,7 +758,7 @@
                         "error": _sanitize_error(e),
                     },
                 )
-                return False
+                return True
 
     def get_user_team_slugs(
         self,
..............................................................F
=================================== FAILURES ===================================
_____________ TestUserHasPermission.test_user_has_permission_error _____________

self = <tests.test_github_client.TestUserHasPermission object at 0x7f68d4d59a70>

    def test_user_has_permission_error(self):
        """Test permission check returns False on error."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_collaborator_permission.side_effect = Exception("API Error")
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.user_has_permission(123456, "owner/repo", "carol", "maintain")
    
>           assert result is False
E           assert True is False

tests/test_github_client.py:756: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 14:02:58 [warning  ] Failed to check collaborator permission for carol in owner/repo: API Error extra={'repo': 'owner/repo', 'username': 'carol', 'installation_id': 123456, 'error': 'API Error'}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestUserHasPermission::test_user_has_permission_error - assert True is False
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 62 passed, 3 deselected in 0.76s
operator: core/ReplaceAndWithOr, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -470,7 +470,7 @@
                 # Find all reviews by bot that are approvals
                 bot_review_ids = []
                 for review in pr.get_reviews():
-                    if review.user.login == bot_user and review.state == "APPROVED":
+                    if review.user.login == bot_user or review.state == "APPROVED":
                         bot_review_ids.append(review.id)
 
                 duration = time.time() - start_time
..........................................................F
=================================== FAILURES ===================================
_______________ TestFindBotReviews.test_find_bot_reviews_success _______________

self = <tests.test_github_client.TestFindBotReviews object at 0x7f4417384cd0>

    def test_find_bot_reviews_success(self):
        """Test successful bot review finding."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration.get_app.return_value = Mock(slug="stampbot")
            mock_integration_cls.return_value = mock_integration
    
            # Create mock reviews
            bot_review = Mock()
            bot_review.user.login = "stampbot[bot]"
            bot_review.state = "APPROVED"
            bot_review.id = 123
    
            other_review = Mock()
            other_review.user.login = "other-user"
            other_review.state = "APPROVED"
            other_review.id = 456
    
            mock_pr = Mock()
            mock_pr.get_reviews.return_value = [bot_review, other_review]
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.find_bot_reviews(123456, "owner/repo", 42)
    
>           assert result == [123]
E           assert [123, 456] == [123]
E             
E             Left contains one more item: 456
E             
E             Full diff:
E               [
E                   123,
E             +     456,
E               ]

tests/test_github_client.py:608: AssertionError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestFindBotReviews::test_find_bot_reviews_success - assert [123, 456] == [123]
  
  Left contains one more item: 456
  
  Full diff:
    [
        123,
  +     456,
    ]
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 58 passed, 3 deselected in 0.76s
operator: core/ReplaceOrWithAnd, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -117,7 +117,7 @@
 
             # Security: prevent path traversal by ensuring the path doesn't escape
             # expected directories and doesn't follow symlinks to unexpected locations
-            if ".." in str(key_path) or not os.path.isfile(key_path):
+            if ".." in str(key_path) and not os.path.isfile(key_path):
                 raise ValueError(f"Invalid private key path: {key}")
 
             try:
............................................F
=================================== FAILURES ===================================
________ TestLoadPrivateKey.test_load_private_key_raises_on_file_error _________

self = <tests.test_github_client.TestLoadPrivateKey object at 0x7f7a84511810>

    def test_load_private_key_raises_on_file_error(self):
        """Test that _load_private_key raises when file doesn't exist."""
        with patch("stampbot.github_client.settings") as mock_settings:
            mock_settings.private_key = "/nonexistent/path/to/key.pem"
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            with pytest.raises(ValueError, match="Invalid private key path"):
>               client._load_private_key()

tests/test_github_client.py:148: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f7a802064e0>

    def _load_private_key(self) -> str:
        """Load private key from settings or file.
    
        Returns:
            Private key contents as PEM string.
    
        Raises:
            RuntimeError: If private key is not configured.
            ValueError: If private key is not valid PEM format.
            OSError: If private key file cannot be read.
        """
        key: str | None = settings.private_key
    
        if key is None:
            raise RuntimeError("Private key not configured")
    
        # If it looks like PEM content, use directly
        if key.startswith("-----BEGIN"):
            pem_content = key
        else:
            # Treat as file path - read the file
            import os
            from pathlib import Path
    
            key_path = Path(key).resolve()
    
            # Security: prevent path traversal by ensuring the path doesn't escape
            # expected directories and doesn't follow symlinks to unexpected locations
            if ".." in str(key_path) and not os.path.isfile(key_path):
                raise ValueError(f"Invalid private key path: {key}")
    
            try:
>               with open(key_path) as f:
                     ^^^^^^^^^^^^^^
E               FileNotFoundError: [Errno 2] No such file or directory: '/nonexistent/path/to/key.pem'

stampbot/github_client.py:124: FileNotFoundError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:29:39 [error    ] Failed to read private key from file: [Errno 2] No such file or directory: '/nonexistent/path/to/key.pem'
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestLoadPrivateKey::test_load_private_key_raises_on_file_error - FileNotFoundError: [Errno 2] No such file or directory: '/nonexistent/path/to/key.pem'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 44 passed, 3 deselected in 0.76s
operator: core/ReplaceOrWithAnd, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -383,7 +383,7 @@
             {
                 "github.repo": repo_full_name,
                 "github.file_path": file_path,
-                "github.ref": ref or "default",
+                "github.ref": ref and "default",
                 "github.installation_id": installation_id,
             },
         ) as span:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceContinueWithBreak, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -827,7 +827,7 @@
                                 "username": username,
                             },
                         )
-                        continue
+                        break
 
                 duration = time.time() - start_time
                 github_api_request_duration_seconds.labels(operation="get_user_teams").observe(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ExceptionReplacer, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -123,7 +123,7 @@
             try:
                 with open(key_path) as f:
                     pem_content = f.read()
-            except Exception as e:
+            except CosmicRayTestingException as e:
                 logger.error("Failed to read private key from file: %s", e)
                 raise
 
.............................................F
=================================== FAILURES ===================================
________ TestLoadPrivateKey.test_load_private_key_raises_on_read_error _________

self = <stampbot.github_client.GitHubAppClient object at 0x7f4fa1c07f20>

    def _load_private_key(self) -> str:
        """Load private key from settings or file.
    
        Returns:
            Private key contents as PEM string.
    
        Raises:
            RuntimeError: If private key is not configured.
            ValueError: If private key is not valid PEM format.
            OSError: If private key file cannot be read.
        """
        key: str | None = settings.private_key
    
        if key is None:
            raise RuntimeError("Private key not configured")
    
        # If it looks like PEM content, use directly
        if key.startswith("-----BEGIN"):
            pem_content = key
        else:
            # Treat as file path - read the file
            import os
            from pathlib import Path
    
            key_path = Path(key).resolve()
    
            # Security: prevent path traversal by ensuring the path doesn't escape
            # expected directories and doesn't follow symlinks to unexpected locations
            if ".." in str(key_path) or not os.path.isfile(key_path):
                raise ValueError(f"Invalid private key path: {key}")
    
            try:
>               with open(key_path) as f:
                     ^^^^^^^^^^^^^^

stampbot/github_client.py:124: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <MagicMock name='open' id='139979993194448'>
args = (PosixPath('/tmp/pytest-of-runner/pytest-597/test_load_private_key_raises_o0/private-key.pem'),)
kwargs = {}, effect = PermissionError('Access denied')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               PermissionError: Access denied

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: PermissionError

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestLoadPrivateKey object at 0x7f4fa5c9c710>
tmp_path = PosixPath('/tmp/pytest-of-runner/pytest-597/test_load_private_key_raises_o0')

    def test_load_private_key_raises_on_read_error(self, tmp_path):
        """Test that _load_private_key raises when file read fails."""
        key_file = tmp_path / "private-key.pem"
        key_file.write_text("dummy")
    
        with patch("stampbot.github_client.settings") as mock_settings:
            mock_settings.private_key = str(key_file)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
    
            # Mock open to raise an exception after the file existence check passes
            with patch("builtins.open", side_effect=PermissionError("Access denied")):
                with pytest.raises(PermissionError, match="Access denied"):
>                   client._load_private_key()

tests/test_github_client.py:165: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f4fa1c07f20>

    def _load_private_key(self) -> str:
        """Load private key from settings or file.
    
        Returns:
            Private key contents as PEM string.
    
        Raises:
            RuntimeError: If private key is not configured.
            ValueError: If private key is not valid PEM format.
            OSError: If private key file cannot be read.
        """
        key: str | None = settings.private_key
    
        if key is None:
            raise RuntimeError("Private key not configured")
    
        # If it looks like PEM content, use directly
        if key.startswith("-----BEGIN"):
            pem_content = key
        else:
            # Treat as file path - read the file
            import os
            from pathlib import Path
    
            key_path = Path(key).resolve()
    
            # Security: prevent path traversal by ensuring the path doesn't escape
            # expected directories and doesn't follow symlinks to unexpected locations
            if ".." in str(key_path) or not os.path.isfile(key_path):
                raise ValueError(f"Invalid private key path: {key}")
    
            try:
                with open(key_path) as f:
                    pem_content = f.read()
>           except CosmicRayTestingException as e:
                   ^^^^^^^^^^^^^^^^^^^^^^^^^
E           NameError: name 'CosmicRayTestingException' is not defined

stampbot/github_client.py:126: NameError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestLoadPrivateKey::test_load_private_key_raises_on_read_error - NameError: name 'CosmicRayTestingException' is not defined
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 45 passed, 3 deselected in 0.85s
operator: core/ExceptionReplacer, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -175,7 +175,7 @@
                 set_span_ok(span)
                 return client
 
-            except Exception as e:
+            except CosmicRayTestingException as e:
                 duration = time.time() - start_time
                 github_api_request_duration_seconds.labels(operation="get_token").observe(duration)
                 github_api_requests_total.labels(operation="get_token", status="failure").inc()
................................................F
=================================== FAILURES ===================================
________ TestGetInstallationClient.test_get_installation_client_failure ________

self = <stampbot.github_client.GitHubAppClient object at 0x7fb9ac0dd230>
installation_id = 123456

    def _get_installation_client(self, installation_id: int) -> Github:
        """Get authenticated GitHub client for an installation.
    
        Args:
            installation_id: GitHub App installation ID.
    
        Returns:
            Authenticated Github client instance with timeout and retry configured.
    
        Raises:
            github.GithubException: If token exchange fails.
        """
        start_time = time.time()
    
        with create_span(
            "github.get_installation_token",
            {"github.installation_id": installation_id},
        ) as span:
            try:
>               auth = self.integration.get_access_token(installation_id)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:155: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='GithubIntegration().get_access_token' id='140435412844032'>
args = (123456,), kwargs = {}, effect = Exception('Token exchange failed')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: Token exchange failed

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestGetInstallationClient object at 0x7fb9aed84190>

    def test_get_installation_client_failure(self):
        """Test installation client creation failure."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_integration.get_access_token.side_effect = Exception("Token exchange failed")
            mock_integration_cls.return_value = mock_integration
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            with pytest.raises(Exception, match="Token exchange failed"):
>               client._get_installation_client(123456)

tests/test_github_client.py:240: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7fb9ac0dd230>
installation_id = 123456

    def _get_installation_client(self, installation_id: int) -> Github:
        """Get authenticated GitHub client for an installation.
    
        Args:
            installation_id: GitHub App installation ID.
    
        Returns:
            Authenticated Github client instance with timeout and retry configured.
    
        Raises:
            github.GithubException: If token exchange fails.
        """
        start_time = time.time()
    
        with create_span(
            "github.get_installation_token",
            {"github.installation_id": installation_id},
        ) as span:
            try:
                auth = self.integration.get_access_token(installation_id)
    
                # Configure retry with exponential backoff
                retry = Retry(
                    total=GITHUB_API_RETRY_TOTAL,
                    backoff_factor=GITHUB_API_RETRY_BACKOFF,
                    status_forcelist=[500, 502, 503, 504],
                    allowed_methods=["GET", "POST", "PUT", "DELETE", "PATCH"],
                )
    
                client = Github(
                    auth=Auth.Token(auth.token),
                    timeout=GITHUB_API_TIMEOUT,
                    retry=retry,
                )
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_token").observe(duration)
                github_api_requests_total.labels(operation="get_token", status="success").inc()
    
                set_span_ok(span)
                return client
    
>           except CosmicRayTestingException as e:
                   ^^^^^^^^^^^^^^^^^^^^^^^^^
E           NameError: name 'CosmicRayTestingException' is not defined

stampbot/github_client.py:178: NameError

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestGetInstallationClient object at 0x7fb9aed84190>

    def test_get_installation_client_failure(self):
        """Test installation client creation failure."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_integration.get_access_token.side_effect = Exception("Token exchange failed")
            mock_integration_cls.return_value = mock_integration
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           with pytest.raises(Exception, match="Token exchange failed"):
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E           AssertionError: Regex pattern did not match.
E             Expected regex: 'Token exchange failed'
E             Actual message: "name 'CosmicRayTestingException' is not defined"

tests/test_github_client.py:239: AssertionError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetInstallationClient::test_get_installation_client_failure - AssertionError: Regex pattern did not match.
  Expected regex: 'Token exchange failed'
  Actual message: "name 'CosmicRayTestingException' is not defined"
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 48 passed, 3 deselected in 0.85s
operator: core/ExceptionReplacer, occurrence: 2
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -197,7 +197,7 @@
             github_api_rate_limit_limit.labels(installation_id=str(installation_id)).set(
                 rate_limit.core.limit  # type: ignore[attr-defined]
             )
-        except Exception:
+        except CosmicRayTestingException:
             # Don't fail operations due to rate limit metric errors
             pass
 
..................................................F
=================================== FAILURES ===================================
_ TestUpdateRateLimitMetrics.test_update_rate_limit_metrics_handles_exception __

self = <stampbot.github_client.GitHubAppClient object at 0x7f4f77a68c50>
client = <Mock id='139979287071440'>, installation_id = 123456

    def _update_rate_limit_metrics(self, client: Github, installation_id: int) -> None:
        """Update rate limit metrics from GitHub client.
    
        Args:
            client: Authenticated GitHub client.
            installation_id: GitHub App installation ID for metric labels.
        """
        try:
>           rate_limit = client.get_rate_limit()
                         ^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:193: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='mock.get_rate_limit' id='139979287071776'>, args = ()
kwargs = {}, effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestUpdateRateLimitMetrics object at 0x7f4f7b97c410>

    def test_update_rate_limit_metrics_handles_exception(self):
        """Test that rate limit metrics errors are silently ignored."""
        from stampbot.github_client import GitHubAppClient
    
        client = GitHubAppClient()
    
        mock_github_client = Mock()
        mock_github_client.get_rate_limit.side_effect = Exception("API Error")
    
        # Should not raise - errors are silently ignored
>       client._update_rate_limit_metrics(mock_github_client, 123456)

tests/test_github_client.py:271: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f4f77a68c50>
client = <Mock id='139979287071440'>, installation_id = 123456

    def _update_rate_limit_metrics(self, client: Github, installation_id: int) -> None:
        """Update rate limit metrics from GitHub client.
    
        Args:
            client: Authenticated GitHub client.
            installation_id: GitHub App installation ID for metric labels.
        """
        try:
            rate_limit = client.get_rate_limit()
            github_api_rate_limit_remaining.labels(installation_id=str(installation_id)).set(
                rate_limit.core.remaining  # type: ignore[attr-defined]
            )
            github_api_rate_limit_limit.labels(installation_id=str(installation_id)).set(
                rate_limit.core.limit  # type: ignore[attr-defined]
            )
>       except CosmicRayTestingException:
               ^^^^^^^^^^^^^^^^^^^^^^^^^
E       NameError: name 'CosmicRayTestingException' is not defined

stampbot/github_client.py:200: NameError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestUpdateRateLimitMetrics::test_update_rate_limit_metrics_handles_exception - NameError: name 'CosmicRayTestingException' is not defined
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 50 passed, 3 deselected in 0.85s
operator: core/ExceptionReplacer, occurrence: 3
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -259,7 +259,7 @@
                 )
                 return True
 
-            except Exception as e:
+            except CosmicRayTestingException as e:
                 duration = time.time() - start_time
                 github_api_request_duration_seconds.labels(operation="approve").observe(duration)
                 github_api_requests_total.labels(operation="approve", status="failure").inc()
....................................................F
=================================== FAILURES ===================================
____________________ TestApprovePR.test_approve_pr_failure _____________________

self = <stampbot.github_client.GitHubAppClient object at 0x7f8718656c50>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
comment = 'Auto-approved by Stampbot'

    def approve_pr(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        comment: str = "Auto-approved by Stampbot",
    ) -> bool:
        """Approve a pull request.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            comment: Review comment
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.approve_pr",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                # Create approval review
>               pr.create_review(
                    body=comment,
                    event="APPROVE",
                )

stampbot/github_client.py:238: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_pull().create_review' id='140218207025344'>
args = (), kwargs = {'body': 'Auto-approved by Stampbot', 'event': 'APPROVE'}
effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestApprovePR object at 0x7f871c6f4690>

    def test_approve_pr_failure(self):
        """Test PR approval failure."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_pr = Mock()
            mock_pr.create_review.side_effect = Exception("API Error")
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.approve_pr(123456, "owner/repo", 42)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:350: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f8718656c50>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
comment = 'Auto-approved by Stampbot'

    def approve_pr(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        comment: str = "Auto-approved by Stampbot",
    ) -> bool:
        """Approve a pull request.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            comment: Review comment
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.approve_pr",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                # Create approval review
                pr.create_review(
                    body=comment,
                    event="APPROVE",
                )
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="approve").observe(duration)
                github_api_requests_total.labels(operation="approve", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.result": "approved"})
                set_span_ok(span)
    
                logger.info(
                    f"Approved PR #{pr_number} in {repo_full_name}",
                    extra={
                        "repo": repo_full_name,
                        "pr_number": pr_number,
                        "installation_id": installation_id,
                    },
                )
                return True
    
>           except CosmicRayTestingException as e:
                   ^^^^^^^^^^^^^^^^^^^^^^^^^
E           NameError: name 'CosmicRayTestingException' is not defined

stampbot/github_client.py:262: NameError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestApprovePR::test_approve_pr_failure - NameError: name 'CosmicRayTestingException' is not defined
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 52 passed, 3 deselected in 0.86s
operator: core/ExceptionReplacer, occurrence: 4
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -337,7 +337,7 @@
                 )
                 return True
 
-            except Exception as e:
+            except CosmicRayTestingException as e:
                 duration = time.time() - start_time
                 github_api_request_duration_seconds.labels(operation="dismiss").observe(duration)
                 github_api_requests_total.labels(operation="dismiss", status="failure").inc()
......................................................F
=================================== FAILURES ===================================
______________ TestDismissApproval.test_dismiss_approval_failure _______________

self = <stampbot.github_client.GitHubAppClient object at 0x7f6f002f2850>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
review_id = 999, message = 'Approval dismissed by Stampbot'

    def dismiss_approval(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        review_id: int,
        message: str = "Approval dismissed by Stampbot",
    ) -> bool:
        """Dismiss a pull request approval.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            review_id: Review ID to dismiss
            message: Dismissal message
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.dismiss_approval",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.review_id": review_id,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                # Get the review and dismiss it
                review = pr.get_review(review_id)
>               review.dismiss(message)

stampbot/github_client.py:318: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_pull().get_review().dismiss' id='140114720060288'>
args = ('Approval dismissed by Stampbot',), kwargs = {}
effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestDismissApproval object at 0x7f6f04590910>

    def test_dismiss_approval_failure(self):
        """Test approval dismissal failure."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_review = Mock()
            mock_review.dismiss.side_effect = Exception("API Error")
            mock_pr = Mock()
            mock_pr.get_review.return_value = mock_review
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.dismiss_approval(123456, "owner/repo", 42, 999)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:435: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f6f002f2850>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
review_id = 999, message = 'Approval dismissed by Stampbot'

    def dismiss_approval(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        review_id: int,
        message: str = "Approval dismissed by Stampbot",
    ) -> bool:
        """Dismiss a pull request approval.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            review_id: Review ID to dismiss
            message: Dismissal message
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.dismiss_approval",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.review_id": review_id,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                # Get the review and dismiss it
                review = pr.get_review(review_id)
                review.dismiss(message)
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="dismiss").observe(duration)
                github_api_requests_total.labels(operation="dismiss", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.result": "dismissed"})
                set_span_ok(span)
    
                logger.info(
                    f"Dismissed approval for PR #{pr_number} in {repo_full_name}",
                    extra={
                        "repo": repo_full_name,
                        "pr_number": pr_number,
                        "installation_id": installation_id,
                        "review_id": review_id,
                    },
                )
                return True
    
>           except CosmicRayTestingException as e:
                   ^^^^^^^^^^^^^^^^^^^^^^^^^
E           NameError: name 'CosmicRayTestingException' is not defined

stampbot/github_client.py:340: NameError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestDismissApproval::test_dismiss_approval_failure - NameError: name 'CosmicRayTestingException' is not defined
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 54 passed, 3 deselected in 0.87s
operator: core/ExceptionReplacer, occurrence: 5
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -413,7 +413,7 @@
 
                 return content.decoded_content.decode("utf-8")
 
-            except Exception as e:
+            except CosmicRayTestingException as e:
                 duration = time.time() - start_time
                 github_api_request_duration_seconds.labels(operation="get_file").observe(duration)
                 github_api_requests_total.labels(operation="get_file", status="not_found").inc()
.........................................................F
=================================== FAILURES ===================================
___________ TestGetRepoFile.test_get_repo_file_returns_none_on_error ___________

self = <stampbot.github_client.GitHubAppClient object at 0x7f947734ccd0>
installation_id = 123456, repo_full_name = 'owner/repo'
file_path = 'nonexistent.toml', ref = None

    def get_repo_file(
        self,
        installation_id: int,
        repo_full_name: str,
        file_path: str,
        ref: str | None = None,
    ) -> str | None:
        """Get file content from repository.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            file_path: Path to file in repository
            ref: Git reference (branch, tag, commit). Defaults to default branch
    
        Returns:
            File content as string, or None if not found
        """
        start_time = time.time()
    
        with create_span(
            "github.get_file",
            {
                "github.repo": repo_full_name,
                "github.file_path": file_path,
                "github.ref": ref or "default",
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
    
>               content = repo.get_contents(file_path, ref=ref)  # type: ignore[arg-type]
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:394: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_contents' id='140275632145808'>
args = ('nonexistent.toml',), kwargs = {'ref': None}
effect = Exception('File not found')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: File not found

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestGetRepoFile object at 0x7f947b68d940>

    def test_get_repo_file_returns_none_on_error(self):
        """Test that get_repo_file returns None when file not found."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_contents.side_effect = Exception("File not found")
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.get_repo_file(123456, "owner/repo", "nonexistent.toml")
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:551: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f947734ccd0>
installation_id = 123456, repo_full_name = 'owner/repo'
file_path = 'nonexistent.toml', ref = None

    def get_repo_file(
        self,
        installation_id: int,
        repo_full_name: str,
        file_path: str,
        ref: str | None = None,
    ) -> str | None:
        """Get file content from repository.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            file_path: Path to file in repository
            ref: Git reference (branch, tag, commit). Defaults to default branch
    
        Returns:
            File content as string, or None if not found
        """
        start_time = time.time()
    
        with create_span(
            "github.get_file",
            {
                "github.repo": repo_full_name,
                "github.file_path": file_path,
                "github.ref": ref or "default",
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
    
                content = repo.get_contents(file_path, ref=ref)  # type: ignore[arg-type]
                if isinstance(content, list):
                    duration = time.time() - start_time
                    github_api_request_duration_seconds.labels(operation="get_file").observe(
                        duration
                    )
                    github_api_requests_total.labels(operation="get_file", status="not_found").inc()
                    add_span_attributes(span, {"github.result": "not_found"})
                    set_span_ok(span)
                    return None
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_file").observe(duration)
                github_api_requests_total.labels(operation="get_file", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.result": "found"})
                set_span_ok(span)
    
                return content.decoded_content.decode("utf-8")
    
>           except CosmicRayTestingException as e:
                   ^^^^^^^^^^^^^^^^^^^^^^^^^
E           NameError: name 'CosmicRayTestingException' is not defined

stampbot/github_client.py:416: NameError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetRepoFile::test_get_repo_file_returns_none_on_error - NameError: name 'CosmicRayTestingException' is not defined
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 57 passed, 3 deselected in 0.87s
operator: core/ExceptionReplacer, occurrence: 6
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -488,7 +488,7 @@
 
                 return bot_review_ids
 
-            except Exception as e:
+            except CosmicRayTestingException as e:
                 duration = time.time() - start_time
                 github_api_request_duration_seconds.labels(operation="find_reviews").observe(
                     duration
...........................................................F
=================================== FAILURES ===================================
_______ TestFindBotReviews.test_find_bot_reviews_returns_empty_on_error ________

self = <stampbot.github_client.GitHubAppClient object at 0x7f336ff6cf30>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42

    def find_bot_reviews(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
    ) -> list[int]:
        """Find all reviews created by this bot on a PR.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
    
        Returns:
            List of review IDs created by the bot
        """
        start_time = time.time()
    
        with create_span(
            "github.find_bot_reviews",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
>               repo = client.get_repo(repo_full_name)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:462: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo' id='139858898993568'>
args = ('owner/repo',), kwargs = {}, effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestFindBotReviews object at 0x7f33743d4e10>

    def test_find_bot_reviews_returns_empty_on_error(self):
        """Test that find_bot_reviews returns empty list on error."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_github = Mock()
            mock_github.get_repo.side_effect = Exception("API Error")
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.find_bot_reviews(123456, "owner/repo", 42)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:640: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f336ff6cf30>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42

    def find_bot_reviews(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
    ) -> list[int]:
        """Find all reviews created by this bot on a PR.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
    
        Returns:
            List of review IDs created by the bot
        """
        start_time = time.time()
    
        with create_span(
            "github.find_bot_reviews",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                # Get bot user via JWT-authenticated integration (installation tokens
                # cannot call GET /user, which is restricted by GitHub Apps integration)
                app_info = self.integration.get_app()
                bot_user = f"{app_info.slug}[bot]"
    
                # Find all reviews by bot that are approvals
                bot_review_ids = []
                for review in pr.get_reviews():
                    if review.user.login == bot_user and review.state == "APPROVED":
                        bot_review_ids.append(review.id)
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="find_reviews").observe(
                    duration
                )
                github_api_requests_total.labels(operation="find_reviews", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(
                    span, {"github.reviews_found": len(bot_review_ids), "github.bot_user": bot_user}
                )
                set_span_ok(span)
    
                return bot_review_ids
    
>           except CosmicRayTestingException as e:
                   ^^^^^^^^^^^^^^^^^^^^^^^^^
E           NameError: name 'CosmicRayTestingException' is not defined

stampbot/github_client.py:491: NameError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestFindBotReviews::test_find_bot_reviews_returns_empty_on_error - NameError: name 'CosmicRayTestingException' is not defined
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 59 passed, 3 deselected in 0.89s
operator: core/ExceptionReplacer, occurrence: 7
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -567,7 +567,7 @@
                 )
                 return True
 
-            except Exception as e:
+            except CosmicRayTestingException as e:
                 duration = time.time() - start_time
                 github_api_request_duration_seconds.labels(operation="comment").observe(duration)
                 github_api_requests_total.labels(operation="comment", status="failure").inc()
........................................................................ [ 34%]
..F
=================================== FAILURES ===================================
_______ TestCreatePrReviewComment.test_create_pr_review_comment_failure ________

self = <stampbot.github_client.GitHubAppClient object at 0x7f54975bf1d0>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
message = 'Config error'

    def create_pr_review_comment(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        message: str,
    ) -> bool:
        """Create a comment review on a pull request.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            message: Review comment body
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.create_review_comment",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
>               pr = repo.get_pull(pr_number)
                     ^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:545: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_pull' id='140001293306576'>
args = (42,), kwargs = {}, effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestCreatePrReviewComment object at 0x7f549bb9d810>

    def test_create_pr_review_comment_failure(self):
        """Test review comment creation handles errors."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_pull.side_effect = Exception("API Error")
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.create_pr_review_comment(
                123456,
                "owner/repo",
                42,
                "Config error",
            )

tests/test_github_client.py:1248: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f54975bf1d0>
installation_id = 123456, repo_full_name = 'owner/repo', pr_number = 42
message = 'Config error'

    def create_pr_review_comment(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        message: str,
    ) -> bool:
        """Create a comment review on a pull request.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            pr_number: Pull request number
            message: Review comment body
    
        Returns:
            True if successful, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.create_review_comment",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                pr = repo.get_pull(pr_number)
    
                pr.create_review(body=message, event="COMMENT")
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="comment").observe(duration)
                github_api_requests_total.labels(operation="comment", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.result": "commented"})
                set_span_ok(span)
    
                logger.info(
                    "Posted config error review on PR #%d in %s",
                    pr_number,
                    repo_full_name,
                    extra={
                        "repo": repo_full_name,
                        "pr_number": pr_number,
                        "installation_id": installation_id,
                    },
                )
                return True
    
>           except CosmicRayTestingException as e:
                   ^^^^^^^^^^^^^^^^^^^^^^^^^
E           NameError: name 'CosmicRayTestingException' is not defined

stampbot/github_client.py:570: NameError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestCreatePrReviewComment::test_create_pr_review_comment_failure - NameError: name 'CosmicRayTestingException' is not defined
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 74 passed, 3 deselected in 0.91s
operator: core/ExceptionReplacer, occurrence: 8
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -629,7 +629,7 @@
                 set_span_ok(span)
                 return True
 
-            except GithubException as e:
+            except CosmicRayTestingException as e:
                 duration = time.time() - start_time
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="failure").inc()
......................................................................F
=================================== FAILURES ===================================
________________ TestRepoHasLabel.test_repo_has_label_not_found ________________

self = <stampbot.github_client.GitHubAppClient object at 0x7f7ed3551370>
installation_id = 123456, repo_full_name = 'owner/repo', label_name = 'missing'

    def repo_has_label(
        self,
        installation_id: int,
        repo_full_name: str,
        label_name: str,
    ) -> bool | None:
        """Check whether a repository has a label.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            label_name: Label name to look up
    
        Returns:
            True if label exists, False if not found, None if unknown due to error
        """
        start_time = time.time()
    
        with create_span(
            "github.get_label",
            {
                "github.repo": repo_full_name,
                "github.label": label_name,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
>               repo.get_label(label_name)

stampbot/github_client.py:620: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_label' id='140182688016304'>
args = ('missing',), kwargs = {}
effect = GithubException(404 {"message": "Not Found"})

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               github.GithubException.GithubException: 404 {"message": "Not Found"}

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: GithubException

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestRepoHasLabel object at 0x7f7ed7c35590>

    def test_repo_has_label_not_found(self):
        """Test repo_has_label returns False when label is missing."""
        from github.GithubException import GithubException
    
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_label.side_effect = GithubException(404, {"message": "Not Found"}, None)
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.repo_has_label(123456, "owner/repo", "missing")
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:1090: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f7ed3551370>
installation_id = 123456, repo_full_name = 'owner/repo', label_name = 'missing'

    def repo_has_label(
        self,
        installation_id: int,
        repo_full_name: str,
        label_name: str,
    ) -> bool | None:
        """Check whether a repository has a label.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            label_name: Label name to look up
    
        Returns:
            True if label exists, False if not found, None if unknown due to error
        """
        start_time = time.time()
    
        with create_span(
            "github.get_label",
            {
                "github.repo": repo_full_name,
                "github.label": label_name,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                repo.get_label(label_name)
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                github_api_requests_total.labels(operation="get_label", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.label_found": True})
                set_span_ok(span)
                return True
    
>           except CosmicRayTestingException as e:
                   ^^^^^^^^^^^^^^^^^^^^^^^^^
E           NameError: name 'CosmicRayTestingException' is not defined

stampbot/github_client.py:632: NameError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestRepoHasLabel::test_repo_has_label_not_found - NameError: name 'CosmicRayTestingException' is not defined
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 70 passed, 3 deselected in 0.91s
operator: core/ExceptionReplacer, occurrence: 9
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -654,7 +654,7 @@
                 )
                 return None
 
-            except Exception as e:
+            except CosmicRayTestingException as e:
                 duration = time.time() - start_time
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="failure").inc()
........................................................................ [ 34%]
F
=================================== FAILURES ===================================
__________________ TestRepoHasLabel.test_repo_has_label_error __________________

self = <stampbot.github_client.GitHubAppClient object at 0x7fbe17c73d70>
installation_id = 123456, repo_full_name = 'owner/repo'
label_name = 'autoapprove'

    def repo_has_label(
        self,
        installation_id: int,
        repo_full_name: str,
        label_name: str,
    ) -> bool | None:
        """Check whether a repository has a label.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            label_name: Label name to look up
    
        Returns:
            True if label exists, False if not found, None if unknown due to error
        """
        start_time = time.time()
    
        with create_span(
            "github.get_label",
            {
                "github.repo": repo_full_name,
                "github.label": label_name,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
>               repo.get_label(label_name)

stampbot/github_client.py:620: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_label' id='140454419338576'>
args = ('autoapprove',), kwargs = {}, effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestRepoHasLabel object at 0x7fbe1c19a060>

    def test_repo_has_label_error(self):
        """Test repo_has_label returns None on error."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_label.side_effect = Exception("API Error")
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.repo_has_label(123456, "owner/repo", "autoapprove")
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:1164: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7fbe17c73d70>
installation_id = 123456, repo_full_name = 'owner/repo'
label_name = 'autoapprove'

    def repo_has_label(
        self,
        installation_id: int,
        repo_full_name: str,
        label_name: str,
    ) -> bool | None:
        """Check whether a repository has a label.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            label_name: Label name to look up
    
        Returns:
            True if label exists, False if not found, None if unknown due to error
        """
        start_time = time.time()
    
        with create_span(
            "github.get_label",
            {
                "github.repo": repo_full_name,
                "github.label": label_name,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
                repo.get_label(label_name)
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                github_api_requests_total.labels(operation="get_label", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(span, {"github.label_found": True})
                set_span_ok(span)
                return True
    
            except GithubException as e:
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                github_api_requests_total.labels(operation="get_label", status="failure").inc()
    
                if e.status == 404:
                    add_span_attributes(span, {"github.label_found": False})
                    set_span_ok(span)
                    return False
    
                set_span_error(span, e)
                logger.debug(
                    "Could not verify label %s in %s: %s",
                    label_name,
                    repo_full_name,
                    _sanitize_error(e),
                    extra={
                        "repo": repo_full_name,
                        "label": label_name,
                        "installation_id": installation_id,
                        "error": _sanitize_error(e),
                    },
                )
                return None
    
>           except CosmicRayTestingException as e:
                   ^^^^^^^^^^^^^^^^^^^^^^^^^
E           NameError: name 'CosmicRayTestingException' is not defined

stampbot/github_client.py:657: NameError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestRepoHasLabel::test_repo_has_label_error - NameError: name 'CosmicRayTestingException' is not defined
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 72 passed, 3 deselected in 0.90s
operator: core/ExceptionReplacer, occurrence: 10
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -712,7 +712,7 @@
                 try:
                     permission_index = permission_order.index(permission)
                     required_index = permission_order.index(required_permission)
-                except ValueError:
+                except CosmicRayTestingException:
                     permission_index = -1
                     required_index = 99
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ExceptionReplacer, occurrence: 11
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -737,7 +737,7 @@
 
                 return has_permission
 
-            except Exception as e:
+            except CosmicRayTestingException as e:
                 duration = time.time() - start_time
                 github_api_request_duration_seconds.labels(operation="get_permission").observe(
                     duration
..............................................................F
=================================== FAILURES ===================================
_____________ TestUserHasPermission.test_user_has_permission_error _____________

self = <stampbot.github_client.GitHubAppClient object at 0x7f81e5b28170>
installation_id = 123456, repo_full_name = 'owner/repo', username = 'carol'
required_permission = 'maintain'

    def user_has_permission(
        self,
        installation_id: int,
        repo_full_name: str,
        username: str,
        required_permission: str,
    ) -> bool:
        """Check whether a user has required access to a repository.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            username: GitHub username to check
            required_permission: Minimum required repository permission
    
        Returns:
            True if user meets or exceeds required permission, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.get_collaborator_permission",
            {
                "github.repo": repo_full_name,
                "github.username": username,
                "github.required_permission": required_permission,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
    
>               permission = repo.get_collaborator_permission(username)
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:710: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_repo().get_collaborator_permission' id='140195881142992'>
args = ('carol',), kwargs = {}, effect = Exception('API Error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API Error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestUserHasPermission object at 0x7f81ea055a70>

    def test_user_has_permission_error(self):
        """Test permission check returns False on error."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_collaborator_permission.side_effect = Exception("API Error")
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.user_has_permission(123456, "owner/repo", "carol", "maintain")
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:754: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f81e5b28170>
installation_id = 123456, repo_full_name = 'owner/repo', username = 'carol'
required_permission = 'maintain'

    def user_has_permission(
        self,
        installation_id: int,
        repo_full_name: str,
        username: str,
        required_permission: str,
    ) -> bool:
        """Check whether a user has required access to a repository.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name (owner/repo)
            username: GitHub username to check
            required_permission: Minimum required repository permission
    
        Returns:
            True if user meets or exceeds required permission, False otherwise
        """
        start_time = time.time()
    
        with create_span(
            "github.get_collaborator_permission",
            {
                "github.repo": repo_full_name,
                "github.username": username,
                "github.required_permission": required_permission,
                "github.installation_id": installation_id,
            },
        ) as span:
            try:
                client = self._get_installation_client(installation_id)
                repo = client.get_repo(repo_full_name)
    
                permission = repo.get_collaborator_permission(username)
                permission_order = ["none", "read", "triage", "write", "maintain", "admin"]
                try:
                    permission_index = permission_order.index(permission)
                    required_index = permission_order.index(required_permission)
                except ValueError:
                    permission_index = -1
                    required_index = 99
    
                has_permission = permission_index >= required_index
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_permission").observe(
                    duration
                )
                github_api_requests_total.labels(operation="get_permission", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(
                    span,
                    {
                        "github.permission": permission,
                        "github.has_permission": has_permission,
                    },
                )
                set_span_ok(span)
    
                return has_permission
    
>           except CosmicRayTestingException as e:
                   ^^^^^^^^^^^^^^^^^^^^^^^^^
E           NameError: name 'CosmicRayTestingException' is not defined

stampbot/github_client.py:740: NameError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestUserHasPermission::test_user_has_permission_error - NameError: name 'CosmicRayTestingException' is not defined
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 62 passed, 3 deselected in 0.88s
operator: core/ExceptionReplacer, occurrence: 12
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -815,7 +815,7 @@
                                     "team": team_slug,
                                 },
                             )
-                    except GithubException as e:
+                    except CosmicRayTestingException as e:
                         # Team not found or no access - skip silently
                         logger.debug(
                             "Could not check team %s membership: %s",
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ExceptionReplacer, occurrence: 13
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -848,7 +848,7 @@
 
                 return member_teams
 
-            except Exception as e:
+            except CosmicRayTestingException as e:
                 duration = time.time() - start_time
                 github_api_request_duration_seconds.labels(operation="get_user_teams").observe(
                     duration
....................................................................F
=================================== FAILURES ===================================
_______ TestGetUserTeamSlugs.test_get_user_team_slugs_general_exception ________

self = <stampbot.github_client.GitHubAppClient object at 0x7f6178911cd0>
installation_id = 123456, org_name = 'acme', username = 'alice'
allowed_teams = ['release-team']

    def get_user_team_slugs(
        self,
        installation_id: int,
        org_name: str,
        username: str,
        allowed_teams: list[str],
    ) -> list[str]:
        """Get team slugs the user is a member of from the allowed teams list.
    
        Args:
            installation_id: GitHub App installation ID
            org_name: Organization name
            username: GitHub username to check
            allowed_teams: List of team slugs to check (can be "org/team" or just "team")
    
        Returns:
            List of team slugs the user is a member of
        """
        start_time = time.time()
    
        with create_span(
            "github.get_user_team_slugs",
            {
                "github.org": org_name,
                "github.username": username,
                "github.installation_id": installation_id,
                "github.teams_to_check": len(allowed_teams),
            },
        ) as span:
            member_teams: list[str] = []
    
            try:
                client = self._get_installation_client(installation_id)
>               org = client.get_organization(org_name)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:796: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_organization' id='140056613156720'>
args = ('acme',), kwargs = {}, effect = Exception('Network error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: Network error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestGetUserTeamSlugs object at 0x7f617cd445f0>

    def test_get_user_team_slugs_general_exception(self):
        """Test team membership check with general exception."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_github = Mock()
            mock_github.get_organization.side_effect = Exception("Network error")
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client.get_user_team_slugs(123456, "acme", "alice", ["release-team"])
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:1010: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.github_client.GitHubAppClient object at 0x7f6178911cd0>
installation_id = 123456, org_name = 'acme', username = 'alice'
allowed_teams = ['release-team']

    def get_user_team_slugs(
        self,
        installation_id: int,
        org_name: str,
        username: str,
        allowed_teams: list[str],
    ) -> list[str]:
        """Get team slugs the user is a member of from the allowed teams list.
    
        Args:
            installation_id: GitHub App installation ID
            org_name: Organization name
            username: GitHub username to check
            allowed_teams: List of team slugs to check (can be "org/team" or just "team")
    
        Returns:
            List of team slugs the user is a member of
        """
        start_time = time.time()
    
        with create_span(
            "github.get_user_team_slugs",
            {
                "github.org": org_name,
                "github.username": username,
                "github.installation_id": installation_id,
                "github.teams_to_check": len(allowed_teams),
            },
        ) as span:
            member_teams: list[str] = []
    
            try:
                client = self._get_installation_client(installation_id)
                org = client.get_organization(org_name)
    
                for team_ref in allowed_teams:
                    # Extract team slug (handle both "org/team" and "team" formats)
                    team_slug = team_ref.split("/")[-1] if "/" in team_ref else team_ref
    
                    try:
                        team = org.get_team_by_slug(team_slug)
                        # Check if user is a member
                        user = client.get_user(username)
                        if team.has_in_members(user):  # type: ignore[arg-type]
                            member_teams.append(team_slug)
                            logger.debug(
                                "User %s is a member of team %s",
                                username,
                                team_slug,
                                extra={
                                    "org": org_name,
                                    "username": username,
                                    "team": team_slug,
                                },
                            )
                    except GithubException as e:
                        # Team not found or no access - skip silently
                        logger.debug(
                            "Could not check team %s membership: %s",
                            team_slug,
                            e.data.get("message", str(e)) if hasattr(e, "data") else str(e),
                            extra={
                                "org": org_name,
                                "team": team_slug,
                                "username": username,
                            },
                        )
                        continue
    
                duration = time.time() - start_time
                github_api_request_duration_seconds.labels(operation="get_user_teams").observe(
                    duration
                )
                github_api_requests_total.labels(operation="get_user_teams", status="success").inc()
    
                self._update_rate_limit_metrics(client, installation_id)
    
                add_span_attributes(
                    span,
                    {
                        "github.member_teams": len(member_teams),
                        "github.teams_checked": len(allowed_teams),
                    },
                )
                set_span_ok(span)
    
                return member_teams
    
>           except CosmicRayTestingException as e:
                   ^^^^^^^^^^^^^^^^^^^^^^^^^
E           NameError: name 'CosmicRayTestingException' is not defined

stampbot/github_client.py:851: NameError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetUserTeamSlugs::test_get_user_team_slugs_general_exception - NameError: name 'CosmicRayTestingException' is not defined
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 68 passed, 3 deselected in 0.89s
operator: core/NumberReplacer, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -21,7 +21,7 @@
 from stampbot.telemetry import add_span_attributes, create_span, set_span_error, set_span_ok
 
 # GitHub API configuration
-GITHUB_API_TIMEOUT = 30  # seconds
+GITHUB_API_TIMEOUT = 31  # seconds
 GITHUB_API_RETRY_TOTAL = 3
 GITHUB_API_RETRY_BACKOFF = 0.5  # exponential backoff factor
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/NumberReplacer, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -21,7 +21,7 @@
 from stampbot.telemetry import add_span_attributes, create_span, set_span_error, set_span_ok
 
 # GitHub API configuration
-GITHUB_API_TIMEOUT = 30  # seconds
+GITHUB_API_TIMEOUT = 29  # seconds
 GITHUB_API_RETRY_TOTAL = 3
 GITHUB_API_RETRY_BACKOFF = 0.5  # exponential backoff factor
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/NumberReplacer, occurrence: 2
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -22,7 +22,7 @@
 
 # GitHub API configuration
 GITHUB_API_TIMEOUT = 30  # seconds
-GITHUB_API_RETRY_TOTAL = 3
+GITHUB_API_RETRY_TOTAL = 4
 GITHUB_API_RETRY_BACKOFF = 0.5  # exponential backoff factor
 
 logger = get_logger(__name__)
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/NumberReplacer, occurrence: 3
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -22,7 +22,7 @@
 
 # GitHub API configuration
 GITHUB_API_TIMEOUT = 30  # seconds
-GITHUB_API_RETRY_TOTAL = 3
+GITHUB_API_RETRY_TOTAL = 2
 GITHUB_API_RETRY_BACKOFF = 0.5  # exponential backoff factor
 
 logger = get_logger(__name__)
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/NumberReplacer, occurrence: 4
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -23,7 +23,7 @@
 # GitHub API configuration
 GITHUB_API_TIMEOUT = 30  # seconds
 GITHUB_API_RETRY_TOTAL = 3
-GITHUB_API_RETRY_BACKOFF = 0.5  # exponential backoff factor
+GITHUB_API_RETRY_BACKOFF = 1.5  # exponential backoff factor
 
 logger = get_logger(__name__)
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.67s
operator: core/NumberReplacer, occurrence: 5
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -23,7 +23,7 @@
 # GitHub API configuration
 GITHUB_API_TIMEOUT = 30  # seconds
 GITHUB_API_RETRY_TOTAL = 3
-GITHUB_API_RETRY_BACKOFF = 0.5  # exponential backoff factor
+GITHUB_API_RETRY_BACKOFF = -0.5  # exponential backoff factor
 
 logger = get_logger(__name__)
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/NumberReplacer, occurrence: 6
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -158,7 +158,7 @@
                 retry = Retry(
                     total=GITHUB_API_RETRY_TOTAL,
                     backoff_factor=GITHUB_API_RETRY_BACKOFF,
-                    status_forcelist=[500, 502, 503, 504],
+                    status_forcelist=[ 501, 502, 503, 504],
                     allowed_methods=["GET", "POST", "PUT", "DELETE", "PATCH"],
                 )
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/NumberReplacer, occurrence: 7
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -158,7 +158,7 @@
                 retry = Retry(
                     total=GITHUB_API_RETRY_TOTAL,
                     backoff_factor=GITHUB_API_RETRY_BACKOFF,
-                    status_forcelist=[500, 502, 503, 504],
+                    status_forcelist=[ 499, 502, 503, 504],
                     allowed_methods=["GET", "POST", "PUT", "DELETE", "PATCH"],
                 )
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/NumberReplacer, occurrence: 8
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -158,7 +158,7 @@
                 retry = Retry(
                     total=GITHUB_API_RETRY_TOTAL,
                     backoff_factor=GITHUB_API_RETRY_BACKOFF,
-                    status_forcelist=[500, 502, 503, 504],
+                    status_forcelist=[500, 503, 503, 504],
                     allowed_methods=["GET", "POST", "PUT", "DELETE", "PATCH"],
                 )
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/NumberReplacer, occurrence: 9
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -158,7 +158,7 @@
                 retry = Retry(
                     total=GITHUB_API_RETRY_TOTAL,
                     backoff_factor=GITHUB_API_RETRY_BACKOFF,
-                    status_forcelist=[500, 502, 503, 504],
+                    status_forcelist=[500, 501, 503, 504],
                     allowed_methods=["GET", "POST", "PUT", "DELETE", "PATCH"],
                 )
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/NumberReplacer, occurrence: 10
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -158,7 +158,7 @@
                 retry = Retry(
                     total=GITHUB_API_RETRY_TOTAL,
                     backoff_factor=GITHUB_API_RETRY_BACKOFF,
-                    status_forcelist=[500, 502, 503, 504],
+                    status_forcelist=[500, 502, 504, 504],
                     allowed_methods=["GET", "POST", "PUT", "DELETE", "PATCH"],
                 )
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/NumberReplacer, occurrence: 11
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -158,7 +158,7 @@
                 retry = Retry(
                     total=GITHUB_API_RETRY_TOTAL,
                     backoff_factor=GITHUB_API_RETRY_BACKOFF,
-                    status_forcelist=[500, 502, 503, 504],
+                    status_forcelist=[500, 502, 502, 504],
                     allowed_methods=["GET", "POST", "PUT", "DELETE", "PATCH"],
                 )
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/NumberReplacer, occurrence: 12
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -158,7 +158,7 @@
                 retry = Retry(
                     total=GITHUB_API_RETRY_TOTAL,
                     backoff_factor=GITHUB_API_RETRY_BACKOFF,
-                    status_forcelist=[500, 502, 503, 504],
+                    status_forcelist=[500, 502, 503, 505],
                     allowed_methods=["GET", "POST", "PUT", "DELETE", "PATCH"],
                 )
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/NumberReplacer, occurrence: 13
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -158,7 +158,7 @@
                 retry = Retry(
                     total=GITHUB_API_RETRY_TOTAL,
                     backoff_factor=GITHUB_API_RETRY_BACKOFF,
-                    status_forcelist=[500, 502, 503, 504],
+                    status_forcelist=[500, 502, 503, 503],
                     allowed_methods=["GET", "POST", "PUT", "DELETE", "PATCH"],
                 )
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/NumberReplacer, occurrence: 14
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -634,7 +634,7 @@
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="failure").inc()
 
-                if e.status == 404:
+                if e.status == 405:
                     add_span_attributes(span, {"github.label_found": False})
                     set_span_ok(span)
                     return False
......................................................................F
=================================== FAILURES ===================================
________________ TestRepoHasLabel.test_repo_has_label_not_found ________________

self = <tests.test_github_client.TestRepoHasLabel object at 0x7fea5f6a5590>

    def test_repo_has_label_not_found(self):
        """Test repo_has_label returns False when label is missing."""
        from github.GithubException import GithubException
    
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_label.side_effect = GithubException(404, {"message": "Not Found"}, None)
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.repo_has_label(123456, "owner/repo", "missing")
    
>           assert result is False
E           assert None is False

tests/test_github_client.py:1092: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:34:16 [debug    ] Could not verify label missing in owner/repo: 404 {"message": "Not Found"} extra={'repo': 'owner/repo', 'label': 'missing', 'installation_id': 123456, 'error': '404 {"message": "Not Found"}'}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestRepoHasLabel::test_repo_has_label_not_found - assert None is False
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 70 passed, 3 deselected in 0.78s
operator: core/NumberReplacer, occurrence: 15
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -634,7 +634,7 @@
                 github_api_request_duration_seconds.labels(operation="get_label").observe(duration)
                 github_api_requests_total.labels(operation="get_label", status="failure").inc()
 
-                if e.status == 404:
+                if e.status == 403:
                     add_span_attributes(span, {"github.label_found": False})
                     set_span_ok(span)
                     return False
......................................................................F
=================================== FAILURES ===================================
________________ TestRepoHasLabel.test_repo_has_label_not_found ________________

self = <tests.test_github_client.TestRepoHasLabel object at 0x7f3ac9791590>

    def test_repo_has_label_not_found(self):
        """Test repo_has_label returns False when label is missing."""
        from github.GithubException import GithubException
    
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_repo = Mock()
            mock_repo.get_label.side_effect = GithubException(404, {"message": "Not Found"}, None)
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.repo_has_label(123456, "owner/repo", "missing")
    
>           assert result is False
E           assert None is False

tests/test_github_client.py:1092: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:41:42 [debug    ] Could not verify label missing in owner/repo: 404 {"message": "Not Found"} extra={'repo': 'owner/repo', 'label': 'missing', 'installation_id': 123456, 'error': '404 {"message": "Not Found"}'}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestRepoHasLabel::test_repo_has_label_not_found - assert None is False
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 70 passed, 3 deselected in 0.78s
operator: core/NumberReplacer, occurrence: 16
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -713,7 +713,7 @@
                     permission_index = permission_order.index(permission)
                     required_index = permission_order.index(required_permission)
                 except ValueError:
-                    permission_index = -1
+                    permission_index = - 2
                     required_index = 99
 
                 has_permission = permission_index >= required_index
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.69s
operator: core/NumberReplacer, occurrence: 17
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -713,7 +713,7 @@
                     permission_index = permission_order.index(permission)
                     required_index = permission_order.index(required_permission)
                 except ValueError:
-                    permission_index = -1
+                    permission_index = - 0
                     required_index = 99
 
                 has_permission = permission_index >= required_index
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.64s
operator: core/NumberReplacer, occurrence: 18
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -714,7 +714,7 @@
                     required_index = permission_order.index(required_permission)
                 except ValueError:
                     permission_index = -1
-                    required_index = 99
+                    required_index = 100
 
                 has_permission = permission_index >= required_index
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/NumberReplacer, occurrence: 19
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -714,7 +714,7 @@
                     required_index = permission_order.index(required_permission)
                 except ValueError:
                     permission_index = -1
-                    required_index = 99
+                    required_index = 98
 
                 has_permission = permission_index >= required_index
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/NumberReplacer, occurrence: 20
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -797,7 +797,7 @@
 
                 for team_ref in allowed_teams:
                     # Extract team slug (handle both "org/team" and "team" formats)
-                    team_slug = team_ref.split("/")[-1] if "/" in team_ref else team_ref
+                    team_slug = team_ref.split("/")[- 2] if "/" in team_ref else team_ref
 
                     try:
                         team = org.get_team_by_slug(team_slug)
.................................................................F
=================================== FAILURES ===================================
________ TestGetUserTeamSlugs.test_get_user_team_slugs_with_org_prefix _________

self = <tests.test_github_client.TestGetUserTeamSlugs object at 0x7f0462f59310>

    def test_get_user_team_slugs_with_org_prefix(self):
        """Test team membership check with org/team format."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_user = Mock()
            mock_team = Mock()
            mock_team.has_in_members.return_value = True
            mock_org = Mock()
            mock_org.get_team_by_slug.return_value = mock_team
    
            mock_github = Mock()
            mock_github.get_organization.return_value = mock_org
            mock_github.get_user.return_value = mock_user
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.get_user_team_slugs(123456, "acme", "alice", ["acme/release-team"])
    
            # Should extract "release-team" from "acme/release-team"
>           mock_org.get_team_by_slug.assert_called_with("release-team")

tests/test_github_client.py:892: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_organization().get_team_by_slug' id='139656748917024'>
args = ('release-team',), kwargs = {}, expected = call('release-team')
actual = call('acme')
_error_message = <function NonCallableMock.assert_called_with.<locals>._error_message at 0x7f045ee24b40>
cause = None

    def assert_called_with(self, /, *args, **kwargs):
        """assert that the last call was made with the specified arguments.
    
        Raises an AssertionError if the args and keyword args passed in are
        different to the last call to the mock."""
        if self.call_args is None:
            expected = self._format_mock_call_signature(args, kwargs)
            actual = 'not called.'
            error_message = ('expected call not found.\nExpected: %s\n  Actual: %s'
                    % (expected, actual))
            raise AssertionError(error_message)
    
        def _error_message():
            msg = self._format_mock_failure_message(args, kwargs)
            return msg
        expected = self._call_matcher(_Call((args, kwargs), two=True))
        actual = self._call_matcher(self.call_args)
        if actual != expected:
            cause = expected if isinstance(expected, Exception) else None
>           raise AssertionError(_error_message()) from cause
E           AssertionError: expected call not found.
E           Expected: get_team_by_slug('release-team')
E             Actual: get_team_by_slug('acme')

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:985: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 14:09:03 [debug    ] User alice is a member of team acme extra={'org': 'acme', 'username': 'alice', 'team': 'acme'}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetUserTeamSlugs::test_get_user_team_slugs_with_org_prefix - AssertionError: expected call not found.
Expected: get_team_by_slug('release-team')
  Actual: get_team_by_slug('acme')
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 65 passed, 3 deselected in 0.81s
operator: core/NumberReplacer, occurrence: 21
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -797,7 +797,7 @@
 
                 for team_ref in allowed_teams:
                     # Extract team slug (handle both "org/team" and "team" formats)
-                    team_slug = team_ref.split("/")[-1] if "/" in team_ref else team_ref
+                    team_slug = team_ref.split("/")[- 0] if "/" in team_ref else team_ref
 
                     try:
                         team = org.get_team_by_slug(team_slug)
.................................................................F
=================================== FAILURES ===================================
________ TestGetUserTeamSlugs.test_get_user_team_slugs_with_org_prefix _________

self = <tests.test_github_client.TestGetUserTeamSlugs object at 0x7f76e0b61310>

    def test_get_user_team_slugs_with_org_prefix(self):
        """Test team membership check with org/team format."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_user = Mock()
            mock_team = Mock()
            mock_team.has_in_members.return_value = True
            mock_org = Mock()
            mock_org.get_team_by_slug.return_value = mock_team
    
            mock_github = Mock()
            mock_github.get_organization.return_value = mock_org
            mock_github.get_user.return_value = mock_user
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.get_user_team_slugs(123456, "acme", "alice", ["acme/release-team"])
    
            # Should extract "release-team" from "acme/release-team"
>           mock_org.get_team_by_slug.assert_called_with("release-team")

tests/test_github_client.py:892: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='Github().get_organization().get_team_by_slug' id='140148484923680'>
args = ('release-team',), kwargs = {}, expected = call('release-team')
actual = call('acme')
_error_message = <function NonCallableMock.assert_called_with.<locals>._error_message at 0x7f76dca28b40>
cause = None

    def assert_called_with(self, /, *args, **kwargs):
        """assert that the last call was made with the specified arguments.
    
        Raises an AssertionError if the args and keyword args passed in are
        different to the last call to the mock."""
        if self.call_args is None:
            expected = self._format_mock_call_signature(args, kwargs)
            actual = 'not called.'
            error_message = ('expected call not found.\nExpected: %s\n  Actual: %s'
                    % (expected, actual))
            raise AssertionError(error_message)
    
        def _error_message():
            msg = self._format_mock_failure_message(args, kwargs)
            return msg
        expected = self._call_matcher(_Call((args, kwargs), two=True))
        actual = self._call_matcher(self.call_args)
        if actual != expected:
            cause = expected if isinstance(expected, Exception) else None
>           raise AssertionError(_error_message()) from cause
E           AssertionError: expected call not found.
E           Expected: get_team_by_slug('release-team')
E             Actual: get_team_by_slug('acme')

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:985: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:48:07 [debug    ] User alice is a member of team acme extra={'org': 'acme', 'username': 'alice', 'team': 'acme'}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetUserTeamSlugs::test_get_user_team_slugs_with_org_prefix - AssertionError: expected call not found.
Expected: get_team_by_slug('release-team')
  Actual: get_team_by_slug('acme')
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 65 passed, 3 deselected in 0.81s
operator: core/RemoveDecorator, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -74,8 +74,6 @@
         self._auth = Auth.AppAuth(app_id, private_key)
         self._integration = GithubIntegration(auth=self._auth)
         self._initialized = True
-
-    @property
     def integration(self) -> GithubIntegration:
         """Get GitHub integration, initializing if needed.
 
........................................F
=================================== FAILURES ===================================
_____ TestIntegrationProperty.test_integration_raises_when_not_initialized _____

self = <tests.test_github_client.TestIntegrationProperty object at 0x7fec599ebd90>

    def test_integration_raises_when_not_initialized(self):
        """Test that integration property raises when not initialized after _ensure_initialized."""
        with patch("stampbot.github_client.is_configured", return_value=False):
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           with pytest.raises(RuntimeError):
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^
E           Failed: DID NOT RAISE <class 'RuntimeError'>

tests/test_github_client.py:94: Failed
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestIntegrationProperty::test_integration_raises_when_not_initialized - Failed: DID NOT RAISE <class 'RuntimeError'>
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 40 passed, 3 deselected in 0.71s
operator: core/ZeroIterationForLoop, occurrence: 0
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -469,7 +469,7 @@
 
                 # Find all reviews by bot that are approvals
                 bot_review_ids = []
-                for review in pr.get_reviews():
+                for review in []:
                     if review.user.login == bot_user and review.state == "APPROVED":
                         bot_review_ids.append(review.id)
 
..........................................................F
=================================== FAILURES ===================================
_______________ TestFindBotReviews.test_find_bot_reviews_success _______________

self = <tests.test_github_client.TestFindBotReviews object at 0x7f602cb1ccd0>

    def test_find_bot_reviews_success(self):
        """Test successful bot review finding."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration.get_app.return_value = Mock(slug="stampbot")
            mock_integration_cls.return_value = mock_integration
    
            # Create mock reviews
            bot_review = Mock()
            bot_review.user.login = "stampbot[bot]"
            bot_review.state = "APPROVED"
            bot_review.id = 123
    
            other_review = Mock()
            other_review.user.login = "other-user"
            other_review.state = "APPROVED"
            other_review.id = 456
    
            mock_pr = Mock()
            mock_pr.get_reviews.return_value = [bot_review, other_review]
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.find_bot_reviews(123456, "owner/repo", 42)
    
>           assert result == [123]
E           assert [] == [123]
E             
E             Right contains one more item: 123
E             
E             Full diff:
E             + []
E             - [
E             -     123,
E             - ]

tests/test_github_client.py:608: AssertionError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestFindBotReviews::test_find_bot_reviews_success - assert [] == [123]
  
  Right contains one more item: 123
  
  Full diff:
  + []
  - [
  -     123,
  - ]
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 58 passed, 3 deselected in 0.76s
operator: core/ZeroIterationForLoop, occurrence: 1
--- mutation diff ---
--- astampbot/github_client.py
+++ bstampbot/github_client.py
@@ -795,7 +795,7 @@
                 client = self._get_installation_client(installation_id)
                 org = client.get_organization(org_name)
 
-                for team_ref in allowed_teams:
+                for team_ref in []:
                     # Extract team slug (handle both "org/team" and "team" formats)
                     team_slug = team_ref.split("/")[-1] if "/" in team_ref else team_ref
 
................................................................F
=================================== FAILURES ===================================
____________ TestGetUserTeamSlugs.test_get_user_team_slugs_success _____________

self = <tests.test_github_client.TestGetUserTeamSlugs object at 0x7fe0a48ed1d0>

    def test_get_user_team_slugs_success(self):
        """Test successful team membership check."""
    
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            # Mock user
            mock_user = Mock()
            mock_user.login = "alice"
    
            # Mock team that has alice as member
            mock_team = Mock()
            mock_team.has_in_members.return_value = True
    
            # Mock org
            mock_org = Mock()
            mock_org.get_team_by_slug.return_value = mock_team
    
            mock_github = Mock()
            mock_github.get_organization.return_value = mock_org
            mock_github.get_user.return_value = mock_user
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.get_user_team_slugs(
                123456, "acme", "alice", ["release-team", "deploy-team"]
            )
    
>           assert result == ["release-team", "deploy-team"]
E           AssertionError: assert [] == ['release-tea...'deploy-team']
E             
E             Right contains 2 more items, first extra item: 'release-team'
E             
E             Full diff:
E             + []
E             - [
E             -     'release-team',
E             -     'deploy-team',
E             - ]

tests/test_github_client.py:848: AssertionError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetUserTeamSlugs::test_get_user_team_slugs_success - AssertionError: assert [] == ['release-tea...'deploy-team']
  
  Right contains 2 more items, first extra item: 'release-team'
  
  Full diff:
  + []
  - [
  -     'release-team',
  -     'deploy-team',
  - ]
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 64 passed, 3 deselected in 0.78s
operator: core/ReplaceBinaryOperator_BitOr_Add, occurrence: 0
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -21,7 +21,7 @@
 logger = get_logger(__name__)
 
 
-def configure_telemetry() -> TracerProvider | None:
+def configure_telemetry() -> TracerProvider + None:
     """Configure OpenTelemetry if enabled.
 
     Returns:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Add, occurrence: 1
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -101,7 +101,7 @@
 @contextmanager
 def create_span(
     name: str,
-    attributes: dict[str, Any] | None = None,
+    attributes: dict[str, Any] + None = None,
     record_exception: bool = True,
 ) -> Iterator[Span | None]:
     """Create a span context manager for tracing operations.
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.66s
operator: core/ReplaceBinaryOperator_BitOr_Add, occurrence: 2
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -103,7 +103,7 @@
     name: str,
     attributes: dict[str, Any] | None = None,
     record_exception: bool = True,
-) -> Iterator[Span | None]:
+) -> Iterator[Span + None]:
     """Create a span context manager for tracing operations.
 
     This is a convenience wrapper that handles span creation, attribute setting,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Add, occurrence: 3
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -143,7 +143,7 @@
             raise
 
 
-def set_span_error(span: Span | None, error: Exception) -> None:
+def set_span_error(span: Span + None, error: Exception) -> None:
     """Set error status on a span.
 
     Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Add, occurrence: 4
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -155,7 +155,7 @@
         span.set_status(Status(StatusCode.ERROR, str(error)))
 
 
-def set_span_ok(span: Span | None) -> None:
+def set_span_ok(span: Span + None) -> None:
     """Set OK status on a span.
 
     Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_Add, occurrence: 5
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -165,7 +165,7 @@
         span.set_status(Status(StatusCode.OK))
 
 
-def add_span_attributes(span: Span | None, attributes: dict[str, Any]) -> None:
+def add_span_attributes(span: Span + None, attributes: dict[str, Any]) -> None:
     """Add attributes to a span.
 
     Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_Sub, occurrence: 0
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -21,7 +21,7 @@
 logger = get_logger(__name__)
 
 
-def configure_telemetry() -> TracerProvider | None:
+def configure_telemetry() -> TracerProvider - None:
     """Configure OpenTelemetry if enabled.
 
     Returns:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Sub, occurrence: 1
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -101,7 +101,7 @@
 @contextmanager
 def create_span(
     name: str,
-    attributes: dict[str, Any] | None = None,
+    attributes: dict[str, Any] - None = None,
     record_exception: bool = True,
 ) -> Iterator[Span | None]:
     """Create a span context manager for tracing operations.
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Sub, occurrence: 2
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -103,7 +103,7 @@
     name: str,
     attributes: dict[str, Any] | None = None,
     record_exception: bool = True,
-) -> Iterator[Span | None]:
+) -> Iterator[Span - None]:
     """Create a span context manager for tracing operations.
 
     This is a convenience wrapper that handles span creation, attribute setting,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Sub, occurrence: 3
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -143,7 +143,7 @@
             raise
 
 
-def set_span_error(span: Span | None, error: Exception) -> None:
+def set_span_error(span: Span - None, error: Exception) -> None:
     """Set error status on a span.
 
     Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/ReplaceBinaryOperator_BitOr_Sub, occurrence: 4
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -155,7 +155,7 @@
         span.set_status(Status(StatusCode.ERROR, str(error)))
 
 
-def set_span_ok(span: Span | None) -> None:
+def set_span_ok(span: Span - None) -> None:
     """Set OK status on a span.
 
     Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Sub, occurrence: 5
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -165,7 +165,7 @@
         span.set_status(Status(StatusCode.OK))
 
 
-def add_span_attributes(span: Span | None, attributes: dict[str, Any]) -> None:
+def add_span_attributes(span: Span - None, attributes: dict[str, Any]) -> None:
     """Add attributes to a span.
 
     Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceBinaryOperator_BitOr_Mul, occurrence: 0
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -21,7 +21,7 @@
 logger = get_logger(__name__)
 
 
-def configure_telemetry() -> TracerProvider | None:
+def configure_telemetry() -> TracerProvider * None:
     """Configure OpenTelemetry if enabled.
 
     Returns:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/ReplaceBinaryOperator_BitOr_Mul, occurrence: 1
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -101,7 +101,7 @@
 @contextmanager
 def create_span(
     name: str,
-    attributes: dict[str, Any] | None = None,
+    attributes: dict[str, Any] * None = None,
     record_exception: bool = True,
 ) -> Iterator[Span | None]:
     """Create a span context manager for tracing operations.
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceBinaryOperator_BitOr_Mul, occurrence: 2
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -103,7 +103,7 @@
     name: str,
     attributes: dict[str, Any] | None = None,
     record_exception: bool = True,
-) -> Iterator[Span | None]:
+) -> Iterator[Span * None]:
     """Create a span context manager for tracing operations.
 
     This is a convenience wrapper that handles span creation, attribute setting,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_Mul, occurrence: 3
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -143,7 +143,7 @@
             raise
 
 
-def set_span_error(span: Span | None, error: Exception) -> None:
+def set_span_error(span: Span * None, error: Exception) -> None:
     """Set error status on a span.
 
     Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Mul, occurrence: 4
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -155,7 +155,7 @@
         span.set_status(Status(StatusCode.ERROR, str(error)))
 
 
-def set_span_ok(span: Span | None) -> None:
+def set_span_ok(span: Span * None) -> None:
     """Set OK status on a span.
 
     Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_Mul, occurrence: 5
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -165,7 +165,7 @@
         span.set_status(Status(StatusCode.OK))
 
 
-def add_span_attributes(span: Span | None, attributes: dict[str, Any]) -> None:
+def add_span_attributes(span: Span * None, attributes: dict[str, Any]) -> None:
     """Add attributes to a span.
 
     Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Div, occurrence: 0
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -21,7 +21,7 @@
 logger = get_logger(__name__)
 
 
-def configure_telemetry() -> TracerProvider | None:
+def configure_telemetry() -> TracerProvider / None:
     """Configure OpenTelemetry if enabled.
 
     Returns:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Div, occurrence: 1
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -101,7 +101,7 @@
 @contextmanager
 def create_span(
     name: str,
-    attributes: dict[str, Any] | None = None,
+    attributes: dict[str, Any] / None = None,
     record_exception: bool = True,
 ) -> Iterator[Span | None]:
     """Create a span context manager for tracing operations.
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceBinaryOperator_BitOr_Div, occurrence: 2
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -103,7 +103,7 @@
     name: str,
     attributes: dict[str, Any] | None = None,
     record_exception: bool = True,
-) -> Iterator[Span | None]:
+) -> Iterator[Span / None]:
     """Create a span context manager for tracing operations.
 
     This is a convenience wrapper that handles span creation, attribute setting,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Div, occurrence: 3
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -143,7 +143,7 @@
             raise
 
 
-def set_span_error(span: Span | None, error: Exception) -> None:
+def set_span_error(span: Span / None, error: Exception) -> None:
     """Set error status on a span.
 
     Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Div, occurrence: 4
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -155,7 +155,7 @@
         span.set_status(Status(StatusCode.ERROR, str(error)))
 
 
-def set_span_ok(span: Span | None) -> None:
+def set_span_ok(span: Span / None) -> None:
     """Set OK status on a span.
 
     Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Div, occurrence: 5
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -165,7 +165,7 @@
         span.set_status(Status(StatusCode.OK))
 
 
-def add_span_attributes(span: Span | None, attributes: dict[str, Any]) -> None:
+def add_span_attributes(span: Span / None, attributes: dict[str, Any]) -> None:
     """Add attributes to a span.
 
     Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/ReplaceBinaryOperator_BitOr_FloorDiv, occurrence: 0
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -21,7 +21,7 @@
 logger = get_logger(__name__)
 
 
-def configure_telemetry() -> TracerProvider | None:
+def configure_telemetry() -> TracerProvider // None:
     """Configure OpenTelemetry if enabled.
 
     Returns:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.65s
operator: core/ReplaceBinaryOperator_BitOr_FloorDiv, occurrence: 1
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -101,7 +101,7 @@
 @contextmanager
 def create_span(
     name: str,
-    attributes: dict[str, Any] | None = None,
+    attributes: dict[str, Any] // None = None,
     record_exception: bool = True,
 ) -> Iterator[Span | None]:
     """Create a span context manager for tracing operations.
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_FloorDiv, occurrence: 2
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -103,7 +103,7 @@
     name: str,
     attributes: dict[str, Any] | None = None,
     record_exception: bool = True,
-) -> Iterator[Span | None]:
+) -> Iterator[Span // None]:
     """Create a span context manager for tracing operations.
 
     This is a convenience wrapper that handles span creation, attribute setting,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_FloorDiv, occurrence: 3
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -143,7 +143,7 @@
             raise
 
 
-def set_span_error(span: Span | None, error: Exception) -> None:
+def set_span_error(span: Span // None, error: Exception) -> None:
     """Set error status on a span.
 
     Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_FloorDiv, occurrence: 4
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -155,7 +155,7 @@
         span.set_status(Status(StatusCode.ERROR, str(error)))
 
 
-def set_span_ok(span: Span | None) -> None:
+def set_span_ok(span: Span // None) -> None:
     """Set OK status on a span.
 
     Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/ReplaceBinaryOperator_BitOr_FloorDiv, occurrence: 5
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -165,7 +165,7 @@
         span.set_status(Status(StatusCode.OK))
 
 
-def add_span_attributes(span: Span | None, attributes: dict[str, Any]) -> None:
+def add_span_attributes(span: Span // None, attributes: dict[str, Any]) -> None:
     """Add attributes to a span.
 
     Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Mod, occurrence: 0
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -21,7 +21,7 @@
 logger = get_logger(__name__)
 
 
-def configure_telemetry() -> TracerProvider | None:
+def configure_telemetry() -> TracerProvider % None:
     """Configure OpenTelemetry if enabled.
 
     Returns:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_Mod, occurrence: 1
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -101,7 +101,7 @@
 @contextmanager
 def create_span(
     name: str,
-    attributes: dict[str, Any] | None = None,
+    attributes: dict[str, Any] % None = None,
     record_exception: bool = True,
 ) -> Iterator[Span | None]:
     """Create a span context manager for tracing operations.
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_Mod, occurrence: 2
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -103,7 +103,7 @@
     name: str,
     attributes: dict[str, Any] | None = None,
     record_exception: bool = True,
-) -> Iterator[Span | None]:
+) -> Iterator[Span % None]:
     """Create a span context manager for tracing operations.
 
     This is a convenience wrapper that handles span creation, attribute setting,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceBinaryOperator_BitOr_Mod, occurrence: 3
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -143,7 +143,7 @@
             raise
 
 
-def set_span_error(span: Span | None, error: Exception) -> None:
+def set_span_error(span: Span % None, error: Exception) -> None:
     """Set error status on a span.
 
     Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_Mod, occurrence: 4
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -155,7 +155,7 @@
         span.set_status(Status(StatusCode.ERROR, str(error)))
 
 
-def set_span_ok(span: Span | None) -> None:
+def set_span_ok(span: Span % None) -> None:
     """Set OK status on a span.
 
     Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Mod, occurrence: 5
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -165,7 +165,7 @@
         span.set_status(Status(StatusCode.OK))
 
 
-def add_span_attributes(span: Span | None, attributes: dict[str, Any]) -> None:
+def add_span_attributes(span: Span % None, attributes: dict[str, Any]) -> None:
     """Add attributes to a span.
 
     Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_Pow, occurrence: 0
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -21,7 +21,7 @@
 logger = get_logger(__name__)
 
 
-def configure_telemetry() -> TracerProvider | None:
+def configure_telemetry() -> TracerProvider ** None:
     """Configure OpenTelemetry if enabled.
 
     Returns:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Pow, occurrence: 1
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -101,7 +101,7 @@
 @contextmanager
 def create_span(
     name: str,
-    attributes: dict[str, Any] | None = None,
+    attributes: dict[str, Any] ** None = None,
     record_exception: bool = True,
 ) -> Iterator[Span | None]:
     """Create a span context manager for tracing operations.
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_Pow, occurrence: 2
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -103,7 +103,7 @@
     name: str,
     attributes: dict[str, Any] | None = None,
     record_exception: bool = True,
-) -> Iterator[Span | None]:
+) -> Iterator[Span ** None]:
     """Create a span context manager for tracing operations.
 
     This is a convenience wrapper that handles span creation, attribute setting,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_Pow, occurrence: 3
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -143,7 +143,7 @@
             raise
 
 
-def set_span_error(span: Span | None, error: Exception) -> None:
+def set_span_error(span: Span ** None, error: Exception) -> None:
     """Set error status on a span.
 
     Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_Pow, occurrence: 4
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -155,7 +155,7 @@
         span.set_status(Status(StatusCode.ERROR, str(error)))
 
 
-def set_span_ok(span: Span | None) -> None:
+def set_span_ok(span: Span ** None) -> None:
     """Set OK status on a span.
 
     Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_Pow, occurrence: 5
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -165,7 +165,7 @@
         span.set_status(Status(StatusCode.OK))
 
 
-def add_span_attributes(span: Span | None, attributes: dict[str, Any]) -> None:
+def add_span_attributes(span: Span ** None, attributes: dict[str, Any]) -> None:
     """Add attributes to a span.
 
     Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_RShift, occurrence: 0
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -21,7 +21,7 @@
 logger = get_logger(__name__)
 
 
-def configure_telemetry() -> TracerProvider | None:
+def configure_telemetry() -> TracerProvider >> None:
     """Configure OpenTelemetry if enabled.
 
     Returns:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_RShift, occurrence: 1
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -101,7 +101,7 @@
 @contextmanager
 def create_span(
     name: str,
-    attributes: dict[str, Any] | None = None,
+    attributes: dict[str, Any] >> None = None,
     record_exception: bool = True,
 ) -> Iterator[Span | None]:
     """Create a span context manager for tracing operations.
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_RShift, occurrence: 2
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -103,7 +103,7 @@
     name: str,
     attributes: dict[str, Any] | None = None,
     record_exception: bool = True,
-) -> Iterator[Span | None]:
+) -> Iterator[Span >> None]:
     """Create a span context manager for tracing operations.
 
     This is a convenience wrapper that handles span creation, attribute setting,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_RShift, occurrence: 3
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -143,7 +143,7 @@
             raise
 
 
-def set_span_error(span: Span | None, error: Exception) -> None:
+def set_span_error(span: Span >> None, error: Exception) -> None:
     """Set error status on a span.
 
     Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_RShift, occurrence: 4
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -155,7 +155,7 @@
         span.set_status(Status(StatusCode.ERROR, str(error)))
 
 
-def set_span_ok(span: Span | None) -> None:
+def set_span_ok(span: Span >> None) -> None:
     """Set OK status on a span.
 
     Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_RShift, occurrence: 5
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -165,7 +165,7 @@
         span.set_status(Status(StatusCode.OK))
 
 
-def add_span_attributes(span: Span | None, attributes: dict[str, Any]) -> None:
+def add_span_attributes(span: Span >> None, attributes: dict[str, Any]) -> None:
     """Add attributes to a span.
 
     Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_LShift, occurrence: 0
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -21,7 +21,7 @@
 logger = get_logger(__name__)
 
 
-def configure_telemetry() -> TracerProvider | None:
+def configure_telemetry() -> TracerProvider << None:
     """Configure OpenTelemetry if enabled.
 
     Returns:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_LShift, occurrence: 1
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -101,7 +101,7 @@
 @contextmanager
 def create_span(
     name: str,
-    attributes: dict[str, Any] | None = None,
+    attributes: dict[str, Any] << None = None,
     record_exception: bool = True,
 ) -> Iterator[Span | None]:
     """Create a span context manager for tracing operations.
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_LShift, occurrence: 2
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -103,7 +103,7 @@
     name: str,
     attributes: dict[str, Any] | None = None,
     record_exception: bool = True,
-) -> Iterator[Span | None]:
+) -> Iterator[Span << None]:
     """Create a span context manager for tracing operations.
 
     This is a convenience wrapper that handles span creation, attribute setting,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.67s
operator: core/ReplaceBinaryOperator_BitOr_LShift, occurrence: 3
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -143,7 +143,7 @@
             raise
 
 
-def set_span_error(span: Span | None, error: Exception) -> None:
+def set_span_error(span: Span << None, error: Exception) -> None:
     """Set error status on a span.
 
     Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceBinaryOperator_BitOr_LShift, occurrence: 4
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -155,7 +155,7 @@
         span.set_status(Status(StatusCode.ERROR, str(error)))
 
 
-def set_span_ok(span: Span | None) -> None:
+def set_span_ok(span: Span << None) -> None:
     """Set OK status on a span.
 
     Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.64s
operator: core/ReplaceBinaryOperator_BitOr_LShift, occurrence: 5
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -165,7 +165,7 @@
         span.set_status(Status(StatusCode.OK))
 
 
-def add_span_attributes(span: Span | None, attributes: dict[str, Any]) -> None:
+def add_span_attributes(span: Span << None, attributes: dict[str, Any]) -> None:
     """Add attributes to a span.
 
     Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_BitAnd, occurrence: 0
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -21,7 +21,7 @@
 logger = get_logger(__name__)
 
 
-def configure_telemetry() -> TracerProvider | None:
+def configure_telemetry() -> TracerProvider & None:
     """Configure OpenTelemetry if enabled.
 
     Returns:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_BitAnd, occurrence: 1
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -101,7 +101,7 @@
 @contextmanager
 def create_span(
     name: str,
-    attributes: dict[str, Any] | None = None,
+    attributes: dict[str, Any] & None = None,
     record_exception: bool = True,
 ) -> Iterator[Span | None]:
     """Create a span context manager for tracing operations.
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_BitAnd, occurrence: 2
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -103,7 +103,7 @@
     name: str,
     attributes: dict[str, Any] | None = None,
     record_exception: bool = True,
-) -> Iterator[Span | None]:
+) -> Iterator[Span & None]:
     """Create a span context manager for tracing operations.
 
     This is a convenience wrapper that handles span creation, attribute setting,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_BitAnd, occurrence: 3
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -143,7 +143,7 @@
             raise
 
 
-def set_span_error(span: Span | None, error: Exception) -> None:
+def set_span_error(span: Span & None, error: Exception) -> None:
     """Set error status on a span.
 
     Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceBinaryOperator_BitOr_BitAnd, occurrence: 4
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -155,7 +155,7 @@
         span.set_status(Status(StatusCode.ERROR, str(error)))
 
 
-def set_span_ok(span: Span | None) -> None:
+def set_span_ok(span: Span & None) -> None:
     """Set OK status on a span.
 
     Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_BitAnd, occurrence: 5
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -165,7 +165,7 @@
         span.set_status(Status(StatusCode.OK))
 
 
-def add_span_attributes(span: Span | None, attributes: dict[str, Any]) -> None:
+def add_span_attributes(span: Span & None, attributes: dict[str, Any]) -> None:
     """Add attributes to a span.
 
     Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_BitXor, occurrence: 0
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -21,7 +21,7 @@
 logger = get_logger(__name__)
 
 
-def configure_telemetry() -> TracerProvider | None:
+def configure_telemetry() -> TracerProvider ^ None:
     """Configure OpenTelemetry if enabled.
 
     Returns:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_BitXor, occurrence: 1
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -101,7 +101,7 @@
 @contextmanager
 def create_span(
     name: str,
-    attributes: dict[str, Any] | None = None,
+    attributes: dict[str, Any] ^ None = None,
     record_exception: bool = True,
 ) -> Iterator[Span | None]:
     """Create a span context manager for tracing operations.
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/ReplaceBinaryOperator_BitOr_BitXor, occurrence: 2
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -103,7 +103,7 @@
     name: str,
     attributes: dict[str, Any] | None = None,
     record_exception: bool = True,
-) -> Iterator[Span | None]:
+) -> Iterator[Span ^ None]:
     """Create a span context manager for tracing operations.
 
     This is a convenience wrapper that handles span creation, attribute setting,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_BitXor, occurrence: 3
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -143,7 +143,7 @@
             raise
 
 
-def set_span_error(span: Span | None, error: Exception) -> None:
+def set_span_error(span: Span ^ None, error: Exception) -> None:
     """Set error status on a span.
 
     Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_BitXor, occurrence: 4
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -155,7 +155,7 @@
         span.set_status(Status(StatusCode.ERROR, str(error)))
 
 
-def set_span_ok(span: Span | None) -> None:
+def set_span_ok(span: Span ^ None) -> None:
     """Set OK status on a span.
 
     Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_BitXor, occurrence: 5
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -165,7 +165,7 @@
         span.set_status(Status(StatusCode.OK))
 
 
-def add_span_attributes(span: Span | None, attributes: dict[str, Any]) -> None:
+def add_span_attributes(span: Span ^ None, attributes: dict[str, Any]) -> None:
     """Add attributes to a span.
 
     Args:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceUnaryOperator_Delete_Not, occurrence: 0
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -27,7 +27,7 @@
     Returns:
         TracerProvider if telemetry is enabled, None otherwise
     """
-    if not settings.otel_enabled:
+    if  settings.otel_enabled:
         logger.info("OpenTelemetry disabled")
         return None
 
........................................................................ [ 34%]
...................F
=================================== FAILURES ===================================
__________________ test_lifespan_startup_shutdown_configured ___________________

    def test_lifespan_startup_shutdown_configured():
        """Test app lifespan startup and shutdown when configured."""
>       from stampbot.main import app

tests/test_main.py:15: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/main.py:45: in <module>
    configure_telemetry()
stampbot/telemetry.py:34: in configure_telemetry
    if not settings.otel_endpoint:
           ^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/dynaconf/base.py:144: in __getattr__
    value = getattr(self._wrapped, name)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <dynaconf.base.Settings object at 0x7f5b7072d400>, name = 'OTEL_ENDPOINT'

    def __getattribute__(self, name):
        if (
            name.startswith("__")
            or name in RESERVED_ATTRS + UPPER_DEFAULT_SETTINGS
        ):
            return super().__getattribute__(name)
    
        # This is to keep the only upper case mode working
        # self._store has Lazy values already evaluated
        if (
            name.islower()
            and self._store.get("LOWERCASE_READ_FOR_DYNACONF", empty) is False
        ):
            try:
                # only matches exact casing, first levels always upper
                return self._store.__getattribute__(name)
            except KeyError:
                return super().__getattribute__(name)
    
        # then go to the regular .get which triggers hooks among other things
        value = self.get(name, default=empty)
        if value is empty:
>           return super().__getattribute__(name)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E           AttributeError: 'Settings' object has no attribute 'OTEL_ENDPOINT'

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/dynaconf/base.py:325: AttributeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_lifespan_startup_shutdown_configured - AttributeError: 'Settings' object has no attribute 'OTEL_ENDPOINT'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 91 passed, 3 deselected in 0.85s
operator: core/ReplaceUnaryOperator_Delete_Not, occurrence: 1
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -31,7 +31,7 @@
         logger.info("OpenTelemetry disabled")
         return None
 
-    if not settings.otel_endpoint:
+    if  settings.otel_endpoint:
         logger.warning("OpenTelemetry enabled but no endpoint configured")
         return None
 
........................................................................ [ 34%]
........................................................................ [ 68%]
........F
=================================== FAILURES ===================================
_____________________ test_configure_telemetry_no_endpoint _____________________

    def test_configure_telemetry_no_endpoint():
        """Test configure_telemetry returns None when no endpoint configured."""
        with patch("stampbot.telemetry.settings") as mock_settings:
            mock_settings.otel_enabled = True
            mock_settings.otel_endpoint = None
            from stampbot.telemetry import configure_telemetry
    
            result = configure_telemetry()
>           assert result is None
E           assert <opentelemetry.sdk.trace.TracerProvider object at 0x7f74c58a97f0> is None

tests/test_telemetry.py:104: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:02:21.936770Z [warning  ] Invalid type MagicMock for attribute 'service.name' value. Expected one of ['bool', 'str', 'bytes', 'int', 'float'] or a sequence of those types
2026-03-27T14:02:21.960539Z [info     ] OpenTelemetry configured with endpoint: None _logger=<_FixedFindCallerLogger stampbot.telemetry (INFO)> _name=info extra={'otel_endpoint': None}
------------------------------ Captured log call -------------------------------
WARNING  opentelemetry.attributes:__init__.py:111 Invalid type MagicMock for attribute 'service.name' value. Expected one of ['bool', 'str', 'bytes', 'int', 'float'] or a sequence of those types
INFO     stampbot.telemetry:telemetry.py:62 {'extra': {'otel_endpoint': None}, 'event': 'OpenTelemetry configured with endpoint: None', 'level': 'info', 'timestamp': '2026-03-27T14:02:21.960539Z'}
=========================== short test summary info ============================
FAILED tests/test_telemetry.py::test_configure_telemetry_no_endpoint - assert <opentelemetry.sdk.trace.TracerProvider object at 0x7f74c58a97f0> is None
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 152 passed, 3 deselected in 1.08s
operator: core/ReplaceUnaryOperator_Delete_Not, occurrence: 2
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -124,7 +124,7 @@
             if span:
                 span.set_attribute("result", "success")
     """
-    if not settings.otel_enabled:
+    if  settings.otel_enabled:
         yield None
         return
 
........................................................................ [ 34%]
........................................................................ [ 68%]
......F
=================================== FAILURES ===================================
_____________________ test_create_span_when_otel_disabled ______________________

    def test_create_span_when_otel_disabled():
        """Test create_span yields None when OTEL is disabled."""
        with patch("stampbot.telemetry.settings") as mock_settings:
            mock_settings.otel_enabled = False
            from stampbot.telemetry import create_span
    
            with create_span("test_span", {"attr": "value"}) as span:
>               assert span is None
E               assert NonRecordingSpan(SpanContext(trace_id=0x00000000000000000000000000000000, span_id=0x0000000000000000, trace_flags=0x00, trace_state=[], is_remote=False)) is None

tests/test_telemetry.py:83: AssertionError
=========================== short test summary info ============================
FAILED tests/test_telemetry.py::test_create_span_when_otel_disabled - assert NonRecordingSpan(SpanContext(trace_id=0x00000000000000000000000000000000, span_id=0x0000000000000000, trace_flags=0x00, trace_state=[], is_remote=False)) is None
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 150 passed, 3 deselected in 1.06s
operator: core/AddNot, occurrence: 0
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -27,7 +27,7 @@
     Returns:
         TracerProvider if telemetry is enabled, None otherwise
     """
-    if not settings.otel_enabled:
+    if not not settings.otel_enabled:
         logger.info("OpenTelemetry disabled")
         return None
 
........................................................................ [ 34%]
...................F
=================================== FAILURES ===================================
__________________ test_lifespan_startup_shutdown_configured ___________________

    def test_lifespan_startup_shutdown_configured():
        """Test app lifespan startup and shutdown when configured."""
>       from stampbot.main import app

tests/test_main.py:15: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/main.py:45: in <module>
    configure_telemetry()
stampbot/telemetry.py:34: in configure_telemetry
    if not settings.otel_endpoint:
           ^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/dynaconf/base.py:144: in __getattr__
    value = getattr(self._wrapped, name)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <dynaconf.base.Settings object at 0x7fac0a6c5400>, name = 'OTEL_ENDPOINT'

    def __getattribute__(self, name):
        if (
            name.startswith("__")
            or name in RESERVED_ATTRS + UPPER_DEFAULT_SETTINGS
        ):
            return super().__getattribute__(name)
    
        # This is to keep the only upper case mode working
        # self._store has Lazy values already evaluated
        if (
            name.islower()
            and self._store.get("LOWERCASE_READ_FOR_DYNACONF", empty) is False
        ):
            try:
                # only matches exact casing, first levels always upper
                return self._store.__getattribute__(name)
            except KeyError:
                return super().__getattribute__(name)
    
        # then go to the regular .get which triggers hooks among other things
        value = self.get(name, default=empty)
        if value is empty:
>           return super().__getattribute__(name)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E           AttributeError: 'Settings' object has no attribute 'OTEL_ENDPOINT'

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/dynaconf/base.py:325: AttributeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_lifespan_startup_shutdown_configured - AttributeError: 'Settings' object has no attribute 'OTEL_ENDPOINT'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 91 passed, 3 deselected in 0.86s
operator: core/AddNot, occurrence: 1
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -31,7 +31,7 @@
         logger.info("OpenTelemetry disabled")
         return None
 
-    if not settings.otel_endpoint:
+    if not not settings.otel_endpoint:
         logger.warning("OpenTelemetry enabled but no endpoint configured")
         return None
 
........................................................................ [ 34%]
........................................................................ [ 68%]
........F
=================================== FAILURES ===================================
_____________________ test_configure_telemetry_no_endpoint _____________________

    def test_configure_telemetry_no_endpoint():
        """Test configure_telemetry returns None when no endpoint configured."""
        with patch("stampbot.telemetry.settings") as mock_settings:
            mock_settings.otel_enabled = True
            mock_settings.otel_endpoint = None
            from stampbot.telemetry import configure_telemetry
    
            result = configure_telemetry()
>           assert result is None
E           assert <opentelemetry.sdk.trace.TracerProvider object at 0x7f64502b17f0> is None

tests/test_telemetry.py:104: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:02:44.106279Z [warning  ] Invalid type MagicMock for attribute 'service.name' value. Expected one of ['bool', 'str', 'bytes', 'int', 'float'] or a sequence of those types
2026-03-27T14:02:44.128925Z [info     ] OpenTelemetry configured with endpoint: None _logger=<_FixedFindCallerLogger stampbot.telemetry (INFO)> _name=info extra={'otel_endpoint': None}
------------------------------ Captured log call -------------------------------
WARNING  opentelemetry.attributes:__init__.py:111 Invalid type MagicMock for attribute 'service.name' value. Expected one of ['bool', 'str', 'bytes', 'int', 'float'] or a sequence of those types
INFO     stampbot.telemetry:telemetry.py:62 {'extra': {'otel_endpoint': None}, 'event': 'OpenTelemetry configured with endpoint: None', 'level': 'info', 'timestamp': '2026-03-27T14:02:44.128925Z'}
=========================== short test summary info ============================
FAILED tests/test_telemetry.py::test_configure_telemetry_no_endpoint - assert <opentelemetry.sdk.trace.TracerProvider object at 0x7f64502b17f0> is None
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 152 passed, 3 deselected in 1.07s
operator: core/AddNot, occurrence: 2
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -78,7 +78,7 @@
     Args:
         app: FastAPI application instance
     """
-    if settings.otel_enabled:
+    if not settings.otel_enabled:
         try:
             FastAPIInstrumentor.instrument_app(app)
             logger.info("FastAPI instrumented with OpenTelemetry")
........................................................................ [ 34%]
........................................................................ [ 68%]
............F
=================================== FAILURES ===================================
_______________________ test_instrument_fastapi_success ________________________

    def test_instrument_fastapi_success():
        """Test instrument_fastapi instruments app when OTEL is enabled."""
        with (
            patch("stampbot.telemetry.settings") as mock_settings,
            patch("stampbot.telemetry.FastAPIInstrumentor") as mock_instrumentor,
        ):
            mock_settings.otel_enabled = True
    
            from stampbot.telemetry import instrument_fastapi
    
            mock_app = Mock()
            instrument_fastapi(mock_app)
    
>           mock_instrumentor.instrument_app.assert_called_once_with(mock_app)

tests/test_telemetry.py:176: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <MagicMock name='FastAPIInstrumentor.instrument_app' id='140072807086608'>
args = (<Mock id='140072807086272'>,), kwargs = {}
msg = "Expected 'instrument_app' to be called once. Called 0 times."

    def assert_called_once_with(self, /, *args, **kwargs):
        """assert that the mock was called exactly once and that that call was
        with the specified arguments."""
        if not self.call_count == 1:
            msg = ("Expected '%s' to be called once. Called %s times.%s"
                   % (self._mock_name or 'mock',
                      self.call_count,
                      self._calls_repr()))
>           raise AssertionError(msg)
E           AssertionError: Expected 'instrument_app' to be called once. Called 0 times.

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:996: AssertionError
=========================== short test summary info ============================
FAILED tests/test_telemetry.py::test_instrument_fastapi_success - AssertionError: Expected 'instrument_app' to be called once. Called 0 times.
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 156 passed, 3 deselected in 1.11s
operator: core/AddNot, occurrence: 3
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -124,7 +124,7 @@
             if span:
                 span.set_attribute("result", "success")
     """
-    if not settings.otel_enabled:
+    if not not settings.otel_enabled:
         yield None
         return
 
........................................................................ [ 34%]
........................................................................ [ 68%]
......F
=================================== FAILURES ===================================
_____________________ test_create_span_when_otel_disabled ______________________

    def test_create_span_when_otel_disabled():
        """Test create_span yields None when OTEL is disabled."""
        with patch("stampbot.telemetry.settings") as mock_settings:
            mock_settings.otel_enabled = False
            from stampbot.telemetry import create_span
    
            with create_span("test_span", {"attr": "value"}) as span:
>               assert span is None
E               assert NonRecordingSpan(SpanContext(trace_id=0x00000000000000000000000000000000, span_id=0x0000000000000000, trace_flags=0x00, trace_state=[], is_remote=False)) is None

tests/test_telemetry.py:83: AssertionError
=========================== short test summary info ============================
FAILED tests/test_telemetry.py::test_create_span_when_otel_disabled - assert NonRecordingSpan(SpanContext(trace_id=0x00000000000000000000000000000000, span_id=0x0000000000000000, trace_flags=0x00, trace_state=[], is_remote=False)) is None
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 150 passed, 3 deselected in 1.05s
operator: core/AddNot, occurrence: 4
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -132,7 +132,7 @@
 
     with tracer.start_as_current_span(name) as span:
         try:
-            if attributes:
+            if not attributes:
                 for key, value in attributes.items():
                     span.set_attribute(key, value)
             yield span
........................................................................ [ 34%]
........................................................................ [ 68%]
..............F
=================================== FAILURES ===================================
_______________________ test_create_span_with_exception ________________________

    def test_create_span_with_exception():
        """Test create_span records exception when one is raised."""
        with (
            patch("stampbot.telemetry.settings") as mock_settings,
            patch("stampbot.telemetry.get_tracer") as mock_get_tracer,
        ):
            mock_settings.otel_enabled = True
    
            mock_span = Mock()
            mock_tracer = Mock()
            mock_tracer.start_as_current_span.return_value.__enter__ = Mock(return_value=mock_span)
            mock_tracer.start_as_current_span.return_value.__exit__ = Mock(return_value=False)
            mock_get_tracer.return_value = mock_tracer
    
            from stampbot.telemetry import create_span
    
            test_error = ValueError("test error")
            try:
>               with create_span("test_span"):
                     ^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_telemetry.py:213: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:141: in __enter__
    return next(self.gen)
           ^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

name = 'test_span', attributes = None, record_exception = True

    @contextmanager
    def create_span(
        name: str,
        attributes: dict[str, Any] | None = None,
        record_exception: bool = True,
    ) -> Iterator[Span | None]:
        """Create a span context manager for tracing operations.
    
        This is a convenience wrapper that handles span creation, attribute setting,
        and exception recording. If OpenTelemetry is disabled, yields None and
        the code block executes without tracing.
    
        Args:
            name: Name of the span
            attributes: Optional attributes to set on the span
            record_exception: Whether to record exceptions on the span
    
        Yields:
            The span if telemetry is enabled, None otherwise
    
        Example:
            with create_span("github.approve_pr", {"repo": "owner/repo", "pr": 123}) as span:
                # do work
                if span:
                    span.set_attribute("result", "success")
        """
        if not settings.otel_enabled:
            yield None
            return
    
        tracer = get_tracer("stampbot")
    
        with tracer.start_as_current_span(name) as span:
            try:
                if not attributes:
>                   for key, value in attributes.items():
                                      ^^^^^^^^^^^^^^^^
E                   AttributeError: 'NoneType' object has no attribute 'items'

stampbot/telemetry.py:136: AttributeError
=========================== short test summary info ============================
FAILED tests/test_telemetry.py::test_create_span_with_exception - AttributeError: 'NoneType' object has no attribute 'items'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 158 passed, 3 deselected in 1.09s
operator: core/AddNot, occurrence: 5
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -137,7 +137,7 @@
                     span.set_attribute(key, value)
             yield span
         except Exception as e:
-            if record_exception:
+            if not record_exception:
                 span.record_exception(e)
                 span.set_status(Status(StatusCode.ERROR, str(e)))
             raise
........................................................................ [ 34%]
........................................................................ [ 68%]
..............F
=================================== FAILURES ===================================
_______________________ test_create_span_with_exception ________________________

    def test_create_span_with_exception():
        """Test create_span records exception when one is raised."""
        with (
            patch("stampbot.telemetry.settings") as mock_settings,
            patch("stampbot.telemetry.get_tracer") as mock_get_tracer,
        ):
            mock_settings.otel_enabled = True
    
            mock_span = Mock()
            mock_tracer = Mock()
            mock_tracer.start_as_current_span.return_value.__enter__ = Mock(return_value=mock_span)
            mock_tracer.start_as_current_span.return_value.__exit__ = Mock(return_value=False)
            mock_get_tracer.return_value = mock_tracer
    
            from stampbot.telemetry import create_span
    
            test_error = ValueError("test error")
            try:
                with create_span("test_span"):
                    raise test_error
            except ValueError:
                pass
    
>           mock_span.record_exception.assert_called_once_with(test_error)

tests/test_telemetry.py:218: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='mock.record_exception' id='140222145291872'>
args = (ValueError('test error'),), kwargs = {}
msg = "Expected 'record_exception' to be called once. Called 0 times."

    def assert_called_once_with(self, /, *args, **kwargs):
        """assert that the mock was called exactly once and that that call was
        with the specified arguments."""
        if not self.call_count == 1:
            msg = ("Expected '%s' to be called once. Called %s times.%s"
                   % (self._mock_name or 'mock',
                      self.call_count,
                      self._calls_repr()))
>           raise AssertionError(msg)
E           AssertionError: Expected 'record_exception' to be called once. Called 0 times.

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:996: AssertionError
=========================== short test summary info ============================
FAILED tests/test_telemetry.py::test_create_span_with_exception - AssertionError: Expected 'record_exception' to be called once. Called 0 times.
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 158 passed, 3 deselected in 1.09s
operator: core/AddNot, occurrence: 6
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -150,7 +150,7 @@
         span: The span to update (can be None if telemetry disabled)
         error: The exception that occurred
     """
-    if span:
+    if not span:
         span.record_exception(error)
         span.set_status(Status(StatusCode.ERROR, str(error)))
 
................................................F
=================================== FAILURES ===================================
________ TestGetInstallationClient.test_get_installation_client_failure ________

self = <stampbot.github_client.GitHubAppClient object at 0x7fd249db9230>
installation_id = 123456

    def _get_installation_client(self, installation_id: int) -> Github:
        """Get authenticated GitHub client for an installation.
    
        Args:
            installation_id: GitHub App installation ID.
    
        Returns:
            Authenticated Github client instance with timeout and retry configured.
    
        Raises:
            github.GithubException: If token exchange fails.
        """
        start_time = time.time()
    
        with create_span(
            "github.get_installation_token",
            {"github.installation_id": installation_id},
        ) as span:
            try:
>               auth = self.integration.get_access_token(installation_id)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

stampbot/github_client.py:155: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='GithubIntegration().get_access_token' id='140541139582464'>
args = (123456,), kwargs = {}, effect = Exception('Token exchange failed')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: Token exchange failed

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestGetInstallationClient object at 0x7fd24cf10190>

    def test_get_installation_client_failure(self):
        """Test installation client creation failure."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_integration.get_access_token.side_effect = Exception("Token exchange failed")
            mock_integration_cls.return_value = mock_integration
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            with pytest.raises(Exception, match="Token exchange failed"):
>               client._get_installation_client(123456)

tests/test_github_client.py:240: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:182: in _get_installation_client
    set_span_error(span, e)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

span = None, error = Exception('Token exchange failed')

    def set_span_error(span: Span | None, error: Exception) -> None:
        """Set error status on a span.
    
        Args:
            span: The span to update (can be None if telemetry disabled)
            error: The exception that occurred
        """
        if not span:
>           span.record_exception(error)
            ^^^^^^^^^^^^^^^^^^^^^
E           AttributeError: 'NoneType' object has no attribute 'record_exception'

stampbot/telemetry.py:154: AttributeError

During handling of the above exception, another exception occurred:

self = <tests.test_github_client.TestGetInstallationClient object at 0x7fd24cf10190>

    def test_get_installation_client_failure(self):
        """Test installation client creation failure."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_integration.get_access_token.side_effect = Exception("Token exchange failed")
            mock_integration_cls.return_value = mock_integration
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           with pytest.raises(Exception, match="Token exchange failed"):
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E           AssertionError: Regex pattern did not match.
E             Expected regex: 'Token exchange failed'
E             Actual message: "'NoneType' object has no attribute 'record_exception'"

tests/test_github_client.py:239: AssertionError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetInstallationClient::test_get_installation_client_failure - AssertionError: Regex pattern did not match.
  Expected regex: 'Token exchange failed'
  Actual message: "'NoneType' object has no attribute 'record_exception'"
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 48 passed, 3 deselected in 0.87s
operator: core/AddNot, occurrence: 7
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -161,7 +161,7 @@
     Args:
         span: The span to update (can be None if telemetry disabled)
     """
-    if span:
+    if not span:
         span.set_status(Status(StatusCode.OK))
 
 
...............................................F
=================================== FAILURES ===================================
________ TestGetInstallationClient.test_get_installation_client_success ________

self = <tests.test_github_client.TestGetInstallationClient object at 0x7f3cf2984050>

    def test_get_installation_client_success(self):
        """Test successful installation client creation."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
>           result = client._get_installation_client(123456)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_github_client.py:211: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/github_client.py:175: in _get_installation_client
    set_span_ok(span)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

span = None

    def set_span_ok(span: Span | None) -> None:
        """Set OK status on a span.
    
        Args:
            span: The span to update (can be None if telemetry disabled)
        """
        if not span:
>           span.set_status(Status(StatusCode.OK))
            ^^^^^^^^^^^^^^^
E           AttributeError: 'NoneType' object has no attribute 'set_status'

stampbot/telemetry.py:165: AttributeError
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestGetInstallationClient::test_get_installation_client_success - AttributeError: 'NoneType' object has no attribute 'set_status'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 47 passed, 3 deselected in 0.76s
operator: core/AddNot, occurrence: 8
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -172,7 +172,7 @@
         span: The span to update (can be None if telemetry disabled)
         attributes: Attributes to add
     """
-    if span:
+    if not span:
         for key, value in attributes.items():
             span.set_attribute(key, value)
 
...................................................F
=================================== FAILURES ===================================
____________________ TestApprovePR.test_approve_pr_success _____________________

self = <tests.test_github_client.TestApprovePR object at 0x7f6d46f74550>

    def test_approve_pr_success(self):
        """Test successful PR approval."""
        with (
            patch("stampbot.github_client.is_configured", return_value=True),
            patch("stampbot.github_client.settings") as mock_settings,
            patch("stampbot.github_client.Auth.AppAuth"),
            patch("stampbot.github_client.GithubIntegration") as mock_integration_cls,
            patch("stampbot.github_client.Github") as mock_github_cls,
            patch("stampbot.github_client.create_span") as mock_span,
        ):
            mock_settings.app_id = 12345
            mock_settings.private_key = TEST_PEM_KEY
            mock_settings.otel_enabled = False
    
            mock_integration = Mock()
            mock_token = Mock()
            mock_token.token = TEST_TOKEN
            mock_integration.get_access_token.return_value = mock_token
            mock_integration_cls.return_value = mock_integration
    
            mock_pr = Mock()
            mock_repo = Mock()
            mock_repo.get_pull.return_value = mock_pr
            mock_github = Mock()
            mock_github.get_repo.return_value = mock_repo
            mock_github.get_rate_limit.return_value = Mock(core=Mock(remaining=4500, limit=5000))
            mock_github_cls.return_value = mock_github
    
            mock_span.return_value.__enter__ = Mock(return_value=None)
            mock_span.return_value.__exit__ = Mock(return_value=False)
    
            from stampbot.github_client import GitHubAppClient
    
            client = GitHubAppClient()
            result = client.approve_pr(123456, "owner/repo", 42)
    
>           assert result is True
E           assert False is True

tests/test_github_client.py:313: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27 13:31:29 [error    ] Failed to approve PR #42 in owner/repo: 'NoneType' object has no attribute 'set_attribute' extra={'repo': 'owner/repo', 'pr_number': 42, 'installation_id': 123456, 'error': "'NoneType' object has no attribute 'set_attribute'"}
=========================== short test summary info ============================
FAILED tests/test_github_client.py::TestApprovePR::test_approve_pr_success - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 51 passed, 3 deselected in 0.73s
operator: core/ReplaceTrueWithFalse, occurrence: 0
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -50,7 +50,7 @@
         # Create OTLP exporter
         otlp_exporter = OTLPSpanExporter(
             endpoint=settings.otel_endpoint,
-            insecure=True,  # Use TLS in production
+            insecure=False,  # Use TLS in production
         )
 
         # Add span processor
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceTrueWithFalse, occurrence: 1
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -102,7 +102,7 @@
 def create_span(
     name: str,
     attributes: dict[str, Any] | None = None,
-    record_exception: bool = True,
+    record_exception: bool = False,
 ) -> Iterator[Span | None]:
     """Create a span context manager for tracing operations.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
..............F
=================================== FAILURES ===================================
_______________________ test_create_span_with_exception ________________________

    def test_create_span_with_exception():
        """Test create_span records exception when one is raised."""
        with (
            patch("stampbot.telemetry.settings") as mock_settings,
            patch("stampbot.telemetry.get_tracer") as mock_get_tracer,
        ):
            mock_settings.otel_enabled = True
    
            mock_span = Mock()
            mock_tracer = Mock()
            mock_tracer.start_as_current_span.return_value.__enter__ = Mock(return_value=mock_span)
            mock_tracer.start_as_current_span.return_value.__exit__ = Mock(return_value=False)
            mock_get_tracer.return_value = mock_tracer
    
            from stampbot.telemetry import create_span
    
            test_error = ValueError("test error")
            try:
                with create_span("test_span"):
                    raise test_error
            except ValueError:
                pass
    
>           mock_span.record_exception.assert_called_once_with(test_error)

tests/test_telemetry.py:218: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Mock name='mock.record_exception' id='139810843453024'>
args = (ValueError('test error'),), kwargs = {}
msg = "Expected 'record_exception' to be called once. Called 0 times."

    def assert_called_once_with(self, /, *args, **kwargs):
        """assert that the mock was called exactly once and that that call was
        with the specified arguments."""
        if not self.call_count == 1:
            msg = ("Expected '%s' to be called once. Called %s times.%s"
                   % (self._mock_name or 'mock',
                      self.call_count,
                      self._calls_repr()))
>           raise AssertionError(msg)
E           AssertionError: Expected 'record_exception' to be called once. Called 0 times.

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:996: AssertionError
=========================== short test summary info ============================
FAILED tests/test_telemetry.py::test_create_span_with_exception - AssertionError: Expected 'record_exception' to be called once. Called 0 times.
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 158 passed, 3 deselected in 1.11s
operator: core/ExceptionReplacer, occurrence: 0
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -67,7 +67,7 @@
 
         return provider
 
-    except Exception as e:
+    except CosmicRayTestingException as e:
         logger.error("Failed to configure OpenTelemetry: %s", e, extra={"error": str(e)})
         return None
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...........F
=================================== FAILURES ===================================
______________________ test_configure_telemetry_exception ______________________

    def configure_telemetry() -> TracerProvider | None:
        """Configure OpenTelemetry if enabled.
    
        Returns:
            TracerProvider if telemetry is enabled, None otherwise
        """
        if not settings.otel_enabled:
            logger.info("OpenTelemetry disabled")
            return None
    
        if not settings.otel_endpoint:
            logger.warning("OpenTelemetry enabled but no endpoint configured")
            return None
    
        try:
            # Create resource
>           resource = Resource.create(
                {
                    "service.name": settings.otel_service_name,
                    "service.version": "0.1.0",
                }
            )

stampbot/telemetry.py:40: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <MagicMock name='Resource.create' id='140071447135968'>
args = ({'service.name': <MagicMock name='settings.otel_service_name' id='140071447136304'>, 'service.version': '0.1.0'},)
kwargs = {}, effect = Exception('Connection failed')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: Connection failed

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

    def test_configure_telemetry_exception():
        """Test configure_telemetry handles exceptions gracefully."""
        with (
            patch("stampbot.telemetry.settings") as mock_settings,
            patch("stampbot.telemetry.Resource") as mock_resource,
        ):
            mock_settings.otel_enabled = True
            mock_settings.otel_endpoint = "http://localhost:4317"
            mock_resource.create.side_effect = Exception("Connection failed")
    
            from stampbot.telemetry import configure_telemetry
    
>           result = configure_telemetry()
                     ^^^^^^^^^^^^^^^^^^^^^

tests/test_telemetry.py:159: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    def configure_telemetry() -> TracerProvider | None:
        """Configure OpenTelemetry if enabled.
    
        Returns:
            TracerProvider if telemetry is enabled, None otherwise
        """
        if not settings.otel_enabled:
            logger.info("OpenTelemetry disabled")
            return None
    
        if not settings.otel_endpoint:
            logger.warning("OpenTelemetry enabled but no endpoint configured")
            return None
    
        try:
            # Create resource
            resource = Resource.create(
                {
                    "service.name": settings.otel_service_name,
                    "service.version": "0.1.0",
                }
            )
    
            # Create tracer provider
            provider = TracerProvider(resource=resource)
    
            # Create OTLP exporter
            otlp_exporter = OTLPSpanExporter(
                endpoint=settings.otel_endpoint,
                insecure=True,  # Use TLS in production
            )
    
            # Add span processor
            provider.add_span_processor(BatchSpanProcessor(otlp_exporter))
    
            # Set as global tracer provider
            trace.set_tracer_provider(provider)
    
            logger.info(
                "OpenTelemetry configured with endpoint: %s",
                settings.otel_endpoint,
                extra={"otel_endpoint": settings.otel_endpoint},
            )
    
            return provider
    
>       except CosmicRayTestingException as e:
               ^^^^^^^^^^^^^^^^^^^^^^^^^
E       NameError: name 'CosmicRayTestingException' is not defined

stampbot/telemetry.py:70: NameError
=========================== short test summary info ============================
FAILED tests/test_telemetry.py::test_configure_telemetry_exception - NameError: name 'CosmicRayTestingException' is not defined
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 155 passed, 3 deselected in 1.17s
operator: core/ExceptionReplacer, occurrence: 1
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -82,7 +82,7 @@
         try:
             FastAPIInstrumentor.instrument_app(app)
             logger.info("FastAPI instrumented with OpenTelemetry")
-        except Exception as e:
+        except CosmicRayTestingException as e:
             logger.error("Failed to instrument FastAPI: %s", e, extra={"error": str(e)})
 
 
........................................................................ [ 34%]
........................................................................ [ 68%]
.............F
=================================== FAILURES ===================================
______________________ test_instrument_fastapi_exception _______________________

app = <Mock id='139773472174976'>

    def instrument_fastapi(app: Any) -> None:
        """Instrument FastAPI app with OpenTelemetry.
    
        Args:
            app: FastAPI application instance
        """
        if settings.otel_enabled:
            try:
>               FastAPIInstrumentor.instrument_app(app)

stampbot/telemetry.py:83: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <MagicMock name='FastAPIInstrumentor.instrument_app' id='139773472175984'>
args = (<Mock id='139773472174976'>,), kwargs = {}
effect = Exception('Instrumentation failed')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: Instrumentation failed

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

    def test_instrument_fastapi_exception():
        """Test instrument_fastapi handles exceptions gracefully."""
        with (
            patch("stampbot.telemetry.settings") as mock_settings,
            patch("stampbot.telemetry.FastAPIInstrumentor") as mock_instrumentor,
        ):
            mock_settings.otel_enabled = True
            mock_instrumentor.instrument_app.side_effect = Exception("Instrumentation failed")
    
            from stampbot.telemetry import instrument_fastapi
    
            mock_app = Mock()
            # Should not raise
>           instrument_fastapi(mock_app)

tests/test_telemetry.py:192: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

app = <Mock id='139773472174976'>

    def instrument_fastapi(app: Any) -> None:
        """Instrument FastAPI app with OpenTelemetry.
    
        Args:
            app: FastAPI application instance
        """
        if settings.otel_enabled:
            try:
                FastAPIInstrumentor.instrument_app(app)
                logger.info("FastAPI instrumented with OpenTelemetry")
>           except CosmicRayTestingException as e:
                   ^^^^^^^^^^^^^^^^^^^^^^^^^
E           NameError: name 'CosmicRayTestingException' is not defined

stampbot/telemetry.py:85: NameError
=========================== short test summary info ============================
FAILED tests/test_telemetry.py::test_instrument_fastapi_exception - NameError: name 'CosmicRayTestingException' is not defined
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 157 passed, 3 deselected in 1.17s
operator: core/ExceptionReplacer, occurrence: 2
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -136,7 +136,7 @@
                 for key, value in attributes.items():
                     span.set_attribute(key, value)
             yield span
-        except Exception as e:
+        except CosmicRayTestingException as e:
             if record_exception:
                 span.record_exception(e)
                 span.set_status(Status(StatusCode.ERROR, str(e)))
........................................................................ [ 34%]
........................................................................ [ 68%]
..............F
=================================== FAILURES ===================================
_______________________ test_create_span_with_exception ________________________

    def test_create_span_with_exception():
        """Test create_span records exception when one is raised."""
        with (
            patch("stampbot.telemetry.settings") as mock_settings,
            patch("stampbot.telemetry.get_tracer") as mock_get_tracer,
        ):
            mock_settings.otel_enabled = True
    
            mock_span = Mock()
            mock_tracer = Mock()
            mock_tracer.start_as_current_span.return_value.__enter__ = Mock(return_value=mock_span)
            mock_tracer.start_as_current_span.return_value.__exit__ = Mock(return_value=False)
            mock_get_tracer.return_value = mock_tracer
    
            from stampbot.telemetry import create_span
    
            test_error = ValueError("test error")
            try:
                with create_span("test_span"):
>                   raise test_error
E                   ValueError: test error

tests/test_telemetry.py:214: ValueError

During handling of the above exception, another exception occurred:

    def test_create_span_with_exception():
        """Test create_span records exception when one is raised."""
        with (
            patch("stampbot.telemetry.settings") as mock_settings,
            patch("stampbot.telemetry.get_tracer") as mock_get_tracer,
        ):
            mock_settings.otel_enabled = True
    
            mock_span = Mock()
            mock_tracer = Mock()
            mock_tracer.start_as_current_span.return_value.__enter__ = Mock(return_value=mock_span)
            mock_tracer.start_as_current_span.return_value.__exit__ = Mock(return_value=False)
            mock_get_tracer.return_value = mock_tracer
    
            from stampbot.telemetry import create_span
    
            test_error = ValueError("test error")
            try:
>               with create_span("test_span"):
                     ^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_telemetry.py:213: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

name = 'test_span', attributes = None, record_exception = True

    @contextmanager
    def create_span(
        name: str,
        attributes: dict[str, Any] | None = None,
        record_exception: bool = True,
    ) -> Iterator[Span | None]:
        """Create a span context manager for tracing operations.
    
        This is a convenience wrapper that handles span creation, attribute setting,
        and exception recording. If OpenTelemetry is disabled, yields None and
        the code block executes without tracing.
    
        Args:
            name: Name of the span
            attributes: Optional attributes to set on the span
            record_exception: Whether to record exceptions on the span
    
        Yields:
            The span if telemetry is enabled, None otherwise
    
        Example:
            with create_span("github.approve_pr", {"repo": "owner/repo", "pr": 123}) as span:
                # do work
                if span:
                    span.set_attribute("result", "success")
        """
        if not settings.otel_enabled:
            yield None
            return
    
        tracer = get_tracer("stampbot")
    
        with tracer.start_as_current_span(name) as span:
            try:
                if attributes:
                    for key, value in attributes.items():
                        span.set_attribute(key, value)
                yield span
>           except CosmicRayTestingException as e:
                   ^^^^^^^^^^^^^^^^^^^^^^^^^
E           NameError: name 'CosmicRayTestingException' is not defined

stampbot/telemetry.py:139: NameError
=========================== short test summary info ============================
FAILED tests/test_telemetry.py::test_create_span_with_exception - NameError: name 'CosmicRayTestingException' is not defined
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 158 passed, 3 deselected in 1.10s
operator: core/RemoveDecorator, occurrence: 0
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -96,9 +96,6 @@
         Tracer instance (no-op if telemetry disabled)
     """
     return trace.get_tracer(name)
-
-
-@contextmanager
 def create_span(
     name: str,
     attributes: dict[str, Any] | None = None,
........................................................................ [ 34%]
.............................F
=================================== FAILURES ===================================
___________________________ test_webhook_valid_ping ____________________________

test_client = <starlette.testclient.TestClient object at 0x7fc1a07d68b0>

    def test_webhook_valid_ping(test_client: TestClient):
        """Test webhook accepts valid ping event."""
        import hashlib
        import hmac
    
        from stampbot.webhook_handler import webhook_handler
    
        body = json.dumps({"zen": "Design for failure."}).encode()
        signature = (
            "sha256="
            + hmac.new(
                webhook_handler.webhook_secret,
                body,
                hashlib.sha256,
            ).hexdigest()
        )
    
        response = test_client.post(
            "/webhook",
            content=body,
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": signature,
            },
        )
>       assert response.status_code == 200
E       assert 500 == 200
E        +  where 500 = <Response [500 Internal Server Error]>.status_code

tests/test_main.py:175: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:37:58.944849Z [error    ] Error handling webhook event: 'generator' object does not support the context manager protocol (missed __exit__ method) _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=error client_ip=testclient extra={'error': "'generator' object does not support the context manager protocol (missed __exit__ method)"}
2026-03-27T13:37:58.945665Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 500 Internal Server Error"
------------------------------ Captured log call -------------------------------
ERROR    stampbot.main:main.py:326 {'extra': {'error': "'generator' object does not support the context manager protocol (missed __exit__ method)"}, 'event': "Error handling webhook event: 'generator' object does not support the context manager protocol (missed __exit__ method)", 'client_ip': 'testclient', 'level': 'error', 'timestamp': '2026-03-27T13:37:58.944849Z'}
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 500 Internal Server Error"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_valid_ping - assert 500 == 200
 +  where 500 = <Response [500 Internal Server Error]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 101 passed, 3 deselected in 0.86s
operator: core/ZeroIterationForLoop, occurrence: 0
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -133,7 +133,7 @@
     with tracer.start_as_current_span(name) as span:
         try:
             if attributes:
-                for key, value in attributes.items():
+                for key, value in []:
                     span.set_attribute(key, value)
             yield span
         except Exception as e:
........................................................................ [ 34%]
........................................................................ [ 68%]
...............F
=================================== FAILURES ===================================
________________ test_create_span_with_attributes_when_enabled _________________

    def test_create_span_with_attributes_when_enabled():
        """Test create_span sets attributes on span when OTEL is enabled."""
        with (
            patch("stampbot.telemetry.settings") as mock_settings,
            patch("stampbot.telemetry.get_tracer") as mock_get_tracer,
        ):
            mock_settings.otel_enabled = True
    
            mock_span = Mock()
            mock_tracer = Mock()
            mock_tracer.start_as_current_span.return_value.__enter__ = Mock(return_value=mock_span)
            mock_tracer.start_as_current_span.return_value.__exit__ = Mock(return_value=False)
            mock_get_tracer.return_value = mock_tracer
    
            from stampbot.telemetry import create_span
    
            with create_span("test_span", {"key1": "value1", "key2": "value2"}) as span:
                assert span == mock_span
    
            # Verify attributes were set on the span
>           assert mock_span.set_attribute.call_count == 2
E           AssertionError: assert 0 == 2
E            +  where 0 = <Mock name='mock.set_attribute' id='140026919312016'>.call_count
E            +    where <Mock name='mock.set_attribute' id='140026919312016'> = <Mock id='140026919313360'>.set_attribute

tests/test_telemetry.py:242: AssertionError
=========================== short test summary info ============================
FAILED tests/test_telemetry.py::test_create_span_with_attributes_when_enabled - AssertionError: assert 0 == 2
 +  where 0 = <Mock name='mock.set_attribute' id='140026919312016'>.call_count
 +    where <Mock name='mock.set_attribute' id='140026919312016'> = <Mock id='140026919313360'>.set_attribute
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 159 passed, 3 deselected in 1.08s
operator: core/ZeroIterationForLoop, occurrence: 1
--- mutation diff ---
--- astampbot/telemetry.py
+++ bstampbot/telemetry.py
@@ -173,6 +173,6 @@
         attributes: Attributes to add
     """
     if span:
-        for key, value in attributes.items():
+        for key, value in []:
             span.set_attribute(key, value)
 
........................................................................ [ 34%]
........................................................................ [ 68%]
.....F
=================================== FAILURES ===================================
______________________ test_add_span_attributes_with_span ______________________

    def test_add_span_attributes_with_span():
        """Test add_span_attributes adds attributes to span."""
        from stampbot.telemetry import add_span_attributes
    
        mock_span = Mock()
        attributes = {"key1": "value1", "key2": "value2"}
    
        add_span_attributes(mock_span, attributes)
    
>       assert mock_span.set_attribute.call_count == 2
E       AssertionError: assert 0 == 2
E        +  where 0 = <Mock name='mock.set_attribute' id='140317167379056'>.call_count
E        +    where <Mock name='mock.set_attribute' id='140317167379056'> = <Mock id='140317167379392'>.set_attribute

tests/test_telemetry.py:71: AssertionError
=========================== short test summary info ============================
FAILED tests/test_telemetry.py::test_add_span_attributes_with_span - AssertionError: assert 0 == 2
 +  where 0 = <Mock name='mock.set_attribute' id='140317167379056'>.call_count
 +    where <Mock name='mock.set_attribute' id='140317167379056'> = <Mock id='140317167379392'>.set_attribute
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 149 passed, 3 deselected in 1.03s
operator: core/ReplaceBinaryOperator_BitOr_Add, occurrence: 0
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -63,7 +63,7 @@
 
 def create_manifest(
     redirect_url: str,
-    webhook_url: str | None = None,
+    webhook_url: str + None = None,
     app_name: str = "Stampbot",
     app_description: str = "GitHub PR Auto-Approval Bot",
 ) -> dict[str, Any]:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Sub, occurrence: 0
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -63,7 +63,7 @@
 
 def create_manifest(
     redirect_url: str,
-    webhook_url: str | None = None,
+    webhook_url: str - None = None,
     app_name: str = "Stampbot",
     app_description: str = "GitHub PR Auto-Approval Bot",
 ) -> dict[str, Any]:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Mul, occurrence: 0
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -63,7 +63,7 @@
 
 def create_manifest(
     redirect_url: str,
-    webhook_url: str | None = None,
+    webhook_url: str * None = None,
     app_name: str = "Stampbot",
     app_description: str = "GitHub PR Auto-Approval Bot",
 ) -> dict[str, Any]:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceBinaryOperator_BitOr_Div, occurrence: 0
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -63,7 +63,7 @@
 
 def create_manifest(
     redirect_url: str,
-    webhook_url: str | None = None,
+    webhook_url: str / None = None,
     app_name: str = "Stampbot",
     app_description: str = "GitHub PR Auto-Approval Bot",
 ) -> dict[str, Any]:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_FloorDiv, occurrence: 0
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -63,7 +63,7 @@
 
 def create_manifest(
     redirect_url: str,
-    webhook_url: str | None = None,
+    webhook_url: str // None = None,
     app_name: str = "Stampbot",
     app_description: str = "GitHub PR Auto-Approval Bot",
 ) -> dict[str, Any]:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/ReplaceBinaryOperator_BitOr_Mod, occurrence: 0
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -63,7 +63,7 @@
 
 def create_manifest(
     redirect_url: str,
-    webhook_url: str | None = None,
+    webhook_url: str % None = None,
     app_name: str = "Stampbot",
     app_description: str = "GitHub PR Auto-Approval Bot",
 ) -> dict[str, Any]:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_Pow, occurrence: 0
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -63,7 +63,7 @@
 
 def create_manifest(
     redirect_url: str,
-    webhook_url: str | None = None,
+    webhook_url: str ** None = None,
     app_name: str = "Stampbot",
     app_description: str = "GitHub PR Auto-Approval Bot",
 ) -> dict[str, Any]:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_RShift, occurrence: 0
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -63,7 +63,7 @@
 
 def create_manifest(
     redirect_url: str,
-    webhook_url: str | None = None,
+    webhook_url: str >> None = None,
     app_name: str = "Stampbot",
     app_description: str = "GitHub PR Auto-Approval Bot",
 ) -> dict[str, Any]:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_LShift, occurrence: 0
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -63,7 +63,7 @@
 
 def create_manifest(
     redirect_url: str,
-    webhook_url: str | None = None,
+    webhook_url: str << None = None,
     app_name: str = "Stampbot",
     app_description: str = "GitHub PR Auto-Approval Bot",
 ) -> dict[str, Any]:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_BitAnd, occurrence: 0
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -63,7 +63,7 @@
 
 def create_manifest(
     redirect_url: str,
-    webhook_url: str | None = None,
+    webhook_url: str & None = None,
     app_name: str = "Stampbot",
     app_description: str = "GitHub PR Auto-Approval Bot",
 ) -> dict[str, Any]:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceBinaryOperator_BitOr_BitXor, occurrence: 0
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -63,7 +63,7 @@
 
 def create_manifest(
     redirect_url: str,
-    webhook_url: str | None = None,
+    webhook_url: str ^ None = None,
     app_name: str = "Stampbot",
     app_description: str = "GitHub PR Auto-Approval Bot",
 ) -> dict[str, Any]:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceComparisonOperator_Eq_NotEq, occurrence: 0
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -31,7 +31,7 @@
         raise ValueError(f"Invalid {name}: must be a complete URL")
 
     # Allow http only for localhost (development)
-    if parsed.scheme == "http":
+    if parsed.scheme != "http":
         if parsed.hostname not in ("localhost", "127.0.0.1", "::1"):
             raise ValueError(f"Invalid {name}: must use HTTPS (HTTP only allowed for localhost)")
     elif parsed.scheme != "https":
........................................................................ [ 34%]
.......................................F
=================================== FAILURES ===================================
_________________ TestUrlValidation.test_allows_http_localhost _________________

self = <tests.test_manifest.TestUrlValidation object at 0x7f45e7679310>

    def test_allows_http_localhost(self):
        """Test that HTTP URLs for localhost are allowed."""
>       manifest = create_manifest(
            redirect_url="http://localhost:8000/setup/callback",
            webhook_url="http://localhost:8000/webhook",
        )

tests/test_manifest.py:32: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/manifest.py:85: in create_manifest
    _validate_url(redirect_url, "redirect_url")
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

url = 'http://localhost:8000/setup/callback', name = 'redirect_url'

    def _validate_url(url: str, name: str) -> None:
        """Validate that a URL is well-formed and uses HTTPS (or localhost).
    
        Args:
            url: URL to validate
            name: Name of the URL parameter for error messages
    
        Raises:
            ValueError: If URL is invalid or doesn't use HTTPS
        """
        parsed = urlparse(url)
    
        if not parsed.scheme or not parsed.netloc:
            raise ValueError(f"Invalid {name}: must be a complete URL")
    
        # Allow http only for localhost (development)
        if parsed.scheme != "http":
            if parsed.hostname not in ("localhost", "127.0.0.1", "::1"):
                raise ValueError(f"Invalid {name}: must use HTTPS (HTTP only allowed for localhost)")
        elif parsed.scheme != "https":
>           raise ValueError(f"Invalid {name}: must use HTTPS")
E           ValueError: Invalid redirect_url: must use HTTPS

stampbot/manifest.py:38: ValueError
=========================== short test summary info ============================
FAILED tests/test_manifest.py::TestUrlValidation::test_allows_http_localhost - ValueError: Invalid redirect_url: must use HTTPS
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 111 passed, 3 deselected in 1.22s
operator: core/ReplaceComparisonOperator_Eq_Lt, occurrence: 0
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -31,7 +31,7 @@
         raise ValueError(f"Invalid {name}: must be a complete URL")
 
     # Allow http only for localhost (development)
-    if parsed.scheme == "http":
+    if parsed.scheme < "http":
         if parsed.hostname not in ("localhost", "127.0.0.1", "::1"):
             raise ValueError(f"Invalid {name}: must use HTTPS (HTTP only allowed for localhost)")
     elif parsed.scheme != "https":
........................................................................ [ 34%]
.......................................F
=================================== FAILURES ===================================
_________________ TestUrlValidation.test_allows_http_localhost _________________

self = <tests.test_manifest.TestUrlValidation object at 0x7f59812c9310>

    def test_allows_http_localhost(self):
        """Test that HTTP URLs for localhost are allowed."""
>       manifest = create_manifest(
            redirect_url="http://localhost:8000/setup/callback",
            webhook_url="http://localhost:8000/webhook",
        )

tests/test_manifest.py:32: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/manifest.py:85: in create_manifest
    _validate_url(redirect_url, "redirect_url")
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

url = 'http://localhost:8000/setup/callback', name = 'redirect_url'

    def _validate_url(url: str, name: str) -> None:
        """Validate that a URL is well-formed and uses HTTPS (or localhost).
    
        Args:
            url: URL to validate
            name: Name of the URL parameter for error messages
    
        Raises:
            ValueError: If URL is invalid or doesn't use HTTPS
        """
        parsed = urlparse(url)
    
        if not parsed.scheme or not parsed.netloc:
            raise ValueError(f"Invalid {name}: must be a complete URL")
    
        # Allow http only for localhost (development)
        if parsed.scheme < "http":
            if parsed.hostname not in ("localhost", "127.0.0.1", "::1"):
                raise ValueError(f"Invalid {name}: must use HTTPS (HTTP only allowed for localhost)")
        elif parsed.scheme != "https":
>           raise ValueError(f"Invalid {name}: must use HTTPS")
E           ValueError: Invalid redirect_url: must use HTTPS

stampbot/manifest.py:38: ValueError
=========================== short test summary info ============================
FAILED tests/test_manifest.py::TestUrlValidation::test_allows_http_localhost - ValueError: Invalid redirect_url: must use HTTPS
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 111 passed, 3 deselected in 0.88s
operator: core/ReplaceComparisonOperator_Eq_LtE, occurrence: 0
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -31,7 +31,7 @@
         raise ValueError(f"Invalid {name}: must be a complete URL")
 
     # Allow http only for localhost (development)
-    if parsed.scheme == "http":
+    if parsed.scheme <= "http":
         if parsed.hostname not in ("localhost", "127.0.0.1", "::1"):
             raise ValueError(f"Invalid {name}: must use HTTPS (HTTP only allowed for localhost)")
     elif parsed.scheme != "https":
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceComparisonOperator_Eq_Gt, occurrence: 0
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -31,7 +31,7 @@
         raise ValueError(f"Invalid {name}: must be a complete URL")
 
     # Allow http only for localhost (development)
-    if parsed.scheme == "http":
+    if parsed.scheme > "http":
         if parsed.hostname not in ("localhost", "127.0.0.1", "::1"):
             raise ValueError(f"Invalid {name}: must use HTTPS (HTTP only allowed for localhost)")
     elif parsed.scheme != "https":
........................................................................ [ 34%]
.......................................F
=================================== FAILURES ===================================
_________________ TestUrlValidation.test_allows_http_localhost _________________

self = <tests.test_manifest.TestUrlValidation object at 0x7f26bf2e1310>

    def test_allows_http_localhost(self):
        """Test that HTTP URLs for localhost are allowed."""
>       manifest = create_manifest(
            redirect_url="http://localhost:8000/setup/callback",
            webhook_url="http://localhost:8000/webhook",
        )

tests/test_manifest.py:32: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/manifest.py:85: in create_manifest
    _validate_url(redirect_url, "redirect_url")
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

url = 'http://localhost:8000/setup/callback', name = 'redirect_url'

    def _validate_url(url: str, name: str) -> None:
        """Validate that a URL is well-formed and uses HTTPS (or localhost).
    
        Args:
            url: URL to validate
            name: Name of the URL parameter for error messages
    
        Raises:
            ValueError: If URL is invalid or doesn't use HTTPS
        """
        parsed = urlparse(url)
    
        if not parsed.scheme or not parsed.netloc:
            raise ValueError(f"Invalid {name}: must be a complete URL")
    
        # Allow http only for localhost (development)
        if parsed.scheme > "http":
            if parsed.hostname not in ("localhost", "127.0.0.1", "::1"):
                raise ValueError(f"Invalid {name}: must use HTTPS (HTTP only allowed for localhost)")
        elif parsed.scheme != "https":
>           raise ValueError(f"Invalid {name}: must use HTTPS")
E           ValueError: Invalid redirect_url: must use HTTPS

stampbot/manifest.py:38: ValueError
=========================== short test summary info ============================
FAILED tests/test_manifest.py::TestUrlValidation::test_allows_http_localhost - ValueError: Invalid redirect_url: must use HTTPS
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 111 passed, 3 deselected in 0.88s
operator: core/ReplaceComparisonOperator_Eq_GtE, occurrence: 0
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -31,7 +31,7 @@
         raise ValueError(f"Invalid {name}: must be a complete URL")
 
     # Allow http only for localhost (development)
-    if parsed.scheme == "http":
+    if parsed.scheme >= "http":
         if parsed.hostname not in ("localhost", "127.0.0.1", "::1"):
             raise ValueError(f"Invalid {name}: must use HTTPS (HTTP only allowed for localhost)")
     elif parsed.scheme != "https":
........................................................................ [ 34%]
..........................................F
=================================== FAILURES ===================================
________ TestCreateManifest.test_manifest_contains_required_permissions ________

self = <tests.test_manifest.TestCreateManifest object at 0x7f292f3856d0>

    def test_manifest_contains_required_permissions(self):
        """Test manifest contains required permissions."""
>       manifest = create_manifest(
            redirect_url="https://example.com/setup/callback",
        )

tests/test_manifest.py:63: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/manifest.py:85: in create_manifest
    _validate_url(redirect_url, "redirect_url")
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

url = 'https://example.com/setup/callback', name = 'redirect_url'

    def _validate_url(url: str, name: str) -> None:
        """Validate that a URL is well-formed and uses HTTPS (or localhost).
    
        Args:
            url: URL to validate
            name: Name of the URL parameter for error messages
    
        Raises:
            ValueError: If URL is invalid or doesn't use HTTPS
        """
        parsed = urlparse(url)
    
        if not parsed.scheme or not parsed.netloc:
            raise ValueError(f"Invalid {name}: must be a complete URL")
    
        # Allow http only for localhost (development)
        if parsed.scheme >= "http":
            if parsed.hostname not in ("localhost", "127.0.0.1", "::1"):
>               raise ValueError(f"Invalid {name}: must use HTTPS (HTTP only allowed for localhost)")
E               ValueError: Invalid redirect_url: must use HTTPS (HTTP only allowed for localhost)

stampbot/manifest.py:36: ValueError
=========================== short test summary info ============================
FAILED tests/test_manifest.py::TestCreateManifest::test_manifest_contains_required_permissions - ValueError: Invalid redirect_url: must use HTTPS (HTTP only allowed for localhost)
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 114 passed, 3 deselected in 0.89s
operator: core/ReplaceComparisonOperator_Eq_Is, occurrence: 0
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -31,7 +31,7 @@
         raise ValueError(f"Invalid {name}: must be a complete URL")
 
     # Allow http only for localhost (development)
-    if parsed.scheme == "http":
+    if parsed.scheme is "http":
         if parsed.hostname not in ("localhost", "127.0.0.1", "::1"):
             raise ValueError(f"Invalid {name}: must use HTTPS (HTTP only allowed for localhost)")
     elif parsed.scheme != "https":
........................................................................ [ 34%]
.......................................F
=================================== FAILURES ===================================
_________________ TestUrlValidation.test_allows_http_localhost _________________

self = <tests.test_manifest.TestUrlValidation object at 0x7feb08ff1310>

    def test_allows_http_localhost(self):
        """Test that HTTP URLs for localhost are allowed."""
>       manifest = create_manifest(
            redirect_url="http://localhost:8000/setup/callback",
            webhook_url="http://localhost:8000/webhook",
        )

tests/test_manifest.py:32: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/manifest.py:85: in create_manifest
    _validate_url(redirect_url, "redirect_url")
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

url = 'http://localhost:8000/setup/callback', name = 'redirect_url'

    def _validate_url(url: str, name: str) -> None:
        """Validate that a URL is well-formed and uses HTTPS (or localhost).
    
        Args:
            url: URL to validate
            name: Name of the URL parameter for error messages
    
        Raises:
            ValueError: If URL is invalid or doesn't use HTTPS
        """
        parsed = urlparse(url)
    
        if not parsed.scheme or not parsed.netloc:
            raise ValueError(f"Invalid {name}: must be a complete URL")
    
        # Allow http only for localhost (development)
        if parsed.scheme is "http":
            if parsed.hostname not in ("localhost", "127.0.0.1", "::1"):
                raise ValueError(f"Invalid {name}: must use HTTPS (HTTP only allowed for localhost)")
        elif parsed.scheme != "https":
>           raise ValueError(f"Invalid {name}: must use HTTPS")
E           ValueError: Invalid redirect_url: must use HTTPS

stampbot/manifest.py:38: ValueError
=============================== warnings summary ===============================
stampbot/manifest.py:34
  /home/runner/work/stampbot/stampbot/stampbot/manifest.py:34: SyntaxWarning: "is" with 'str' literal. Did you mean "=="?
    if parsed.scheme is "http":

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=========================== short test summary info ============================
FAILED tests/test_manifest.py::TestUrlValidation::test_allows_http_localhost - ValueError: Invalid redirect_url: must use HTTPS
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 111 passed, 3 deselected, 1 warning in 0.89s
operator: core/ReplaceComparisonOperator_Eq_IsNot, occurrence: 0
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -31,7 +31,7 @@
         raise ValueError(f"Invalid {name}: must be a complete URL")
 
     # Allow http only for localhost (development)
-    if parsed.scheme == "http":
+    if parsed.scheme is not "http":
         if parsed.hostname not in ("localhost", "127.0.0.1", "::1"):
             raise ValueError(f"Invalid {name}: must use HTTPS (HTTP only allowed for localhost)")
     elif parsed.scheme != "https":
........................................................................ [ 34%]
..........................................F
=================================== FAILURES ===================================
________ TestCreateManifest.test_manifest_contains_required_permissions ________

self = <tests.test_manifest.TestCreateManifest object at 0x7f9cafa716d0>

    def test_manifest_contains_required_permissions(self):
        """Test manifest contains required permissions."""
>       manifest = create_manifest(
            redirect_url="https://example.com/setup/callback",
        )

tests/test_manifest.py:63: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/manifest.py:85: in create_manifest
    _validate_url(redirect_url, "redirect_url")
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

url = 'https://example.com/setup/callback', name = 'redirect_url'

    def _validate_url(url: str, name: str) -> None:
        """Validate that a URL is well-formed and uses HTTPS (or localhost).
    
        Args:
            url: URL to validate
            name: Name of the URL parameter for error messages
    
        Raises:
            ValueError: If URL is invalid or doesn't use HTTPS
        """
        parsed = urlparse(url)
    
        if not parsed.scheme or not parsed.netloc:
            raise ValueError(f"Invalid {name}: must be a complete URL")
    
        # Allow http only for localhost (development)
        if parsed.scheme is not "http":
            if parsed.hostname not in ("localhost", "127.0.0.1", "::1"):
>               raise ValueError(f"Invalid {name}: must use HTTPS (HTTP only allowed for localhost)")
E               ValueError: Invalid redirect_url: must use HTTPS (HTTP only allowed for localhost)

stampbot/manifest.py:36: ValueError
=============================== warnings summary ===============================
stampbot/manifest.py:34
  /home/runner/work/stampbot/stampbot/stampbot/manifest.py:34: SyntaxWarning: "is not" with 'str' literal. Did you mean "!="?
    if parsed.scheme is not "http":

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=========================== short test summary info ============================
FAILED tests/test_manifest.py::TestCreateManifest::test_manifest_contains_required_permissions - ValueError: Invalid redirect_url: must use HTTPS (HTTP only allowed for localhost)
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 114 passed, 3 deselected, 1 warning in 0.87s
operator: core/ReplaceComparisonOperator_NotEq_Eq, occurrence: 0
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -34,7 +34,7 @@
     if parsed.scheme == "http":
         if parsed.hostname not in ("localhost", "127.0.0.1", "::1"):
             raise ValueError(f"Invalid {name}: must use HTTPS (HTTP only allowed for localhost)")
-    elif parsed.scheme != "https":
+    elif parsed.scheme == "https":
         raise ValueError(f"Invalid {name}: must use HTTPS")
 
 
........................................................................ [ 34%]
..........................................F
=================================== FAILURES ===================================
________ TestCreateManifest.test_manifest_contains_required_permissions ________

self = <tests.test_manifest.TestCreateManifest object at 0x7fc9f2f456d0>

    def test_manifest_contains_required_permissions(self):
        """Test manifest contains required permissions."""
>       manifest = create_manifest(
            redirect_url="https://example.com/setup/callback",
        )

tests/test_manifest.py:63: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/manifest.py:85: in create_manifest
    _validate_url(redirect_url, "redirect_url")
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

url = 'https://example.com/setup/callback', name = 'redirect_url'

    def _validate_url(url: str, name: str) -> None:
        """Validate that a URL is well-formed and uses HTTPS (or localhost).
    
        Args:
            url: URL to validate
            name: Name of the URL parameter for error messages
    
        Raises:
            ValueError: If URL is invalid or doesn't use HTTPS
        """
        parsed = urlparse(url)
    
        if not parsed.scheme or not parsed.netloc:
            raise ValueError(f"Invalid {name}: must be a complete URL")
    
        # Allow http only for localhost (development)
        if parsed.scheme == "http":
            if parsed.hostname not in ("localhost", "127.0.0.1", "::1"):
                raise ValueError(f"Invalid {name}: must use HTTPS (HTTP only allowed for localhost)")
        elif parsed.scheme == "https":
>           raise ValueError(f"Invalid {name}: must use HTTPS")
E           ValueError: Invalid redirect_url: must use HTTPS

stampbot/manifest.py:38: ValueError
=========================== short test summary info ============================
FAILED tests/test_manifest.py::TestCreateManifest::test_manifest_contains_required_permissions - ValueError: Invalid redirect_url: must use HTTPS
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 114 passed, 3 deselected in 0.88s
operator: core/ReplaceComparisonOperator_NotEq_Lt, occurrence: 0
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -34,7 +34,7 @@
     if parsed.scheme == "http":
         if parsed.hostname not in ("localhost", "127.0.0.1", "::1"):
             raise ValueError(f"Invalid {name}: must use HTTPS (HTTP only allowed for localhost)")
-    elif parsed.scheme != "https":
+    elif parsed.scheme < "https":
         raise ValueError(f"Invalid {name}: must use HTTPS")
 
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceComparisonOperator_NotEq_LtE, occurrence: 0
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -34,7 +34,7 @@
     if parsed.scheme == "http":
         if parsed.hostname not in ("localhost", "127.0.0.1", "::1"):
             raise ValueError(f"Invalid {name}: must use HTTPS (HTTP only allowed for localhost)")
-    elif parsed.scheme != "https":
+    elif parsed.scheme <= "https":
         raise ValueError(f"Invalid {name}: must use HTTPS")
 
 
........................................................................ [ 34%]
..........................................F
=================================== FAILURES ===================================
________ TestCreateManifest.test_manifest_contains_required_permissions ________

self = <tests.test_manifest.TestCreateManifest object at 0x7f50b5ed16d0>

    def test_manifest_contains_required_permissions(self):
        """Test manifest contains required permissions."""
>       manifest = create_manifest(
            redirect_url="https://example.com/setup/callback",
        )

tests/test_manifest.py:63: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/manifest.py:85: in create_manifest
    _validate_url(redirect_url, "redirect_url")
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

url = 'https://example.com/setup/callback', name = 'redirect_url'

    def _validate_url(url: str, name: str) -> None:
        """Validate that a URL is well-formed and uses HTTPS (or localhost).
    
        Args:
            url: URL to validate
            name: Name of the URL parameter for error messages
    
        Raises:
            ValueError: If URL is invalid or doesn't use HTTPS
        """
        parsed = urlparse(url)
    
        if not parsed.scheme or not parsed.netloc:
            raise ValueError(f"Invalid {name}: must be a complete URL")
    
        # Allow http only for localhost (development)
        if parsed.scheme == "http":
            if parsed.hostname not in ("localhost", "127.0.0.1", "::1"):
                raise ValueError(f"Invalid {name}: must use HTTPS (HTTP only allowed for localhost)")
        elif parsed.scheme <= "https":
>           raise ValueError(f"Invalid {name}: must use HTTPS")
E           ValueError: Invalid redirect_url: must use HTTPS

stampbot/manifest.py:38: ValueError
=========================== short test summary info ============================
FAILED tests/test_manifest.py::TestCreateManifest::test_manifest_contains_required_permissions - ValueError: Invalid redirect_url: must use HTTPS
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 114 passed, 3 deselected in 0.89s
operator: core/ReplaceComparisonOperator_NotEq_Gt, occurrence: 0
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -34,7 +34,7 @@
     if parsed.scheme == "http":
         if parsed.hostname not in ("localhost", "127.0.0.1", "::1"):
             raise ValueError(f"Invalid {name}: must use HTTPS (HTTP only allowed for localhost)")
-    elif parsed.scheme != "https":
+    elif parsed.scheme > "https":
         raise ValueError(f"Invalid {name}: must use HTTPS")
 
 
........................................................................ [ 34%]
.........................................F
=================================== FAILURES ===================================
_______________ TestUrlValidation.test_rejects_non_https_scheme ________________

self = <tests.test_manifest.TestUrlValidation object at 0x7f46a29e28b0>

    def test_rejects_non_https_scheme(self):
        """Test that non-HTTPS schemes are rejected for webhook URLs."""
        import pytest
    
>       with pytest.raises(ValueError, match="must use HTTPS"):
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E       Failed: DID NOT RAISE <class 'ValueError'>

tests/test_manifest.py:51: Failed
=========================== short test summary info ============================
FAILED tests/test_manifest.py::TestUrlValidation::test_rejects_non_https_scheme - Failed: DID NOT RAISE <class 'ValueError'>
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 113 passed, 3 deselected in 0.88s
operator: core/ReplaceComparisonOperator_NotEq_GtE, occurrence: 0
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -34,7 +34,7 @@
     if parsed.scheme == "http":
         if parsed.hostname not in ("localhost", "127.0.0.1", "::1"):
             raise ValueError(f"Invalid {name}: must use HTTPS (HTTP only allowed for localhost)")
-    elif parsed.scheme != "https":
+    elif parsed.scheme >= "https":
         raise ValueError(f"Invalid {name}: must use HTTPS")
 
 
........................................................................ [ 34%]
..........................................F
=================================== FAILURES ===================================
________ TestCreateManifest.test_manifest_contains_required_permissions ________

self = <tests.test_manifest.TestCreateManifest object at 0x7fc1ceccd6d0>

    def test_manifest_contains_required_permissions(self):
        """Test manifest contains required permissions."""
>       manifest = create_manifest(
            redirect_url="https://example.com/setup/callback",
        )

tests/test_manifest.py:63: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/manifest.py:85: in create_manifest
    _validate_url(redirect_url, "redirect_url")
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

url = 'https://example.com/setup/callback', name = 'redirect_url'

    def _validate_url(url: str, name: str) -> None:
        """Validate that a URL is well-formed and uses HTTPS (or localhost).
    
        Args:
            url: URL to validate
            name: Name of the URL parameter for error messages
    
        Raises:
            ValueError: If URL is invalid or doesn't use HTTPS
        """
        parsed = urlparse(url)
    
        if not parsed.scheme or not parsed.netloc:
            raise ValueError(f"Invalid {name}: must be a complete URL")
    
        # Allow http only for localhost (development)
        if parsed.scheme == "http":
            if parsed.hostname not in ("localhost", "127.0.0.1", "::1"):
                raise ValueError(f"Invalid {name}: must use HTTPS (HTTP only allowed for localhost)")
        elif parsed.scheme >= "https":
>           raise ValueError(f"Invalid {name}: must use HTTPS")
E           ValueError: Invalid redirect_url: must use HTTPS

stampbot/manifest.py:38: ValueError
=========================== short test summary info ============================
FAILED tests/test_manifest.py::TestCreateManifest::test_manifest_contains_required_permissions - ValueError: Invalid redirect_url: must use HTTPS
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 114 passed, 3 deselected in 0.89s
operator: core/ReplaceComparisonOperator_NotEq_Is, occurrence: 0
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -34,7 +34,7 @@
     if parsed.scheme == "http":
         if parsed.hostname not in ("localhost", "127.0.0.1", "::1"):
             raise ValueError(f"Invalid {name}: must use HTTPS (HTTP only allowed for localhost)")
-    elif parsed.scheme != "https":
+    elif parsed.scheme is "https":
         raise ValueError(f"Invalid {name}: must use HTTPS")
 
 
........................................................................ [ 34%]
.........................................F
=================================== FAILURES ===================================
_______________ TestUrlValidation.test_rejects_non_https_scheme ________________

self = <tests.test_manifest.TestUrlValidation object at 0x7f9c381ca8b0>

    def test_rejects_non_https_scheme(self):
        """Test that non-HTTPS schemes are rejected for webhook URLs."""
        import pytest
    
>       with pytest.raises(ValueError, match="must use HTTPS"):
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E       Failed: DID NOT RAISE <class 'ValueError'>

tests/test_manifest.py:51: Failed
=============================== warnings summary ===============================
stampbot/manifest.py:37
  /home/runner/work/stampbot/stampbot/stampbot/manifest.py:37: SyntaxWarning: "is" with 'str' literal. Did you mean "=="?
    elif parsed.scheme is "https":

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=========================== short test summary info ============================
FAILED tests/test_manifest.py::TestUrlValidation::test_rejects_non_https_scheme - Failed: DID NOT RAISE <class 'ValueError'>
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 113 passed, 3 deselected, 1 warning in 0.88s
operator: core/ReplaceComparisonOperator_NotEq_IsNot, occurrence: 0
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -34,7 +34,7 @@
     if parsed.scheme == "http":
         if parsed.hostname not in ("localhost", "127.0.0.1", "::1"):
             raise ValueError(f"Invalid {name}: must use HTTPS (HTTP only allowed for localhost)")
-    elif parsed.scheme != "https":
+    elif parsed.scheme is not "https":
         raise ValueError(f"Invalid {name}: must use HTTPS")
 
 
........................................................................ [ 34%]
..........................................F
=================================== FAILURES ===================================
________ TestCreateManifest.test_manifest_contains_required_permissions ________

self = <tests.test_manifest.TestCreateManifest object at 0x7fa01e9596d0>

    def test_manifest_contains_required_permissions(self):
        """Test manifest contains required permissions."""
>       manifest = create_manifest(
            redirect_url="https://example.com/setup/callback",
        )

tests/test_manifest.py:63: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/manifest.py:85: in create_manifest
    _validate_url(redirect_url, "redirect_url")
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

url = 'https://example.com/setup/callback', name = 'redirect_url'

    def _validate_url(url: str, name: str) -> None:
        """Validate that a URL is well-formed and uses HTTPS (or localhost).
    
        Args:
            url: URL to validate
            name: Name of the URL parameter for error messages
    
        Raises:
            ValueError: If URL is invalid or doesn't use HTTPS
        """
        parsed = urlparse(url)
    
        if not parsed.scheme or not parsed.netloc:
            raise ValueError(f"Invalid {name}: must be a complete URL")
    
        # Allow http only for localhost (development)
        if parsed.scheme == "http":
            if parsed.hostname not in ("localhost", "127.0.0.1", "::1"):
                raise ValueError(f"Invalid {name}: must use HTTPS (HTTP only allowed for localhost)")
        elif parsed.scheme is not "https":
>           raise ValueError(f"Invalid {name}: must use HTTPS")
E           ValueError: Invalid redirect_url: must use HTTPS

stampbot/manifest.py:38: ValueError
=============================== warnings summary ===============================
stampbot/manifest.py:37
  /home/runner/work/stampbot/stampbot/stampbot/manifest.py:37: SyntaxWarning: "is not" with 'str' literal. Did you mean "!="?
    elif parsed.scheme is not "https":

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=========================== short test summary info ============================
FAILED tests/test_manifest.py::TestCreateManifest::test_manifest_contains_required_permissions - ValueError: Invalid redirect_url: must use HTTPS
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 114 passed, 3 deselected, 1 warning in 0.88s
operator: core/ReplaceUnaryOperator_Delete_Not, occurrence: 0
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -27,7 +27,7 @@
     """
     parsed = urlparse(url)
 
-    if not parsed.scheme or not parsed.netloc:
+    if  parsed.scheme or not parsed.netloc:
         raise ValueError(f"Invalid {name}: must be a complete URL")
 
     # Allow http only for localhost (development)
........................................................................ [ 34%]
......................................F
=================================== FAILURES ===================================
__________ TestUrlValidation.test_rejects_http_non_localhost_redirect __________

self = <tests.test_manifest.TestUrlValidation object at 0x7f560aa26c10>

    def test_rejects_http_non_localhost_redirect(self):
        """Test that HTTP redirect URLs for non-localhost are rejected."""
        import pytest
    
        with pytest.raises(ValueError, match="must use HTTPS"):
>           create_manifest(
                redirect_url="http://example.com/setup/callback",
            )

tests/test_manifest.py:26: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/manifest.py:85: in create_manifest
    _validate_url(redirect_url, "redirect_url")
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

url = 'http://example.com/setup/callback', name = 'redirect_url'

    def _validate_url(url: str, name: str) -> None:
        """Validate that a URL is well-formed and uses HTTPS (or localhost).
    
        Args:
            url: URL to validate
            name: Name of the URL parameter for error messages
    
        Raises:
            ValueError: If URL is invalid or doesn't use HTTPS
        """
        parsed = urlparse(url)
    
        if  parsed.scheme or not parsed.netloc:
>           raise ValueError(f"Invalid {name}: must be a complete URL")
E           ValueError: Invalid redirect_url: must be a complete URL

stampbot/manifest.py:31: ValueError

During handling of the above exception, another exception occurred:

self = <tests.test_manifest.TestUrlValidation object at 0x7f560aa26c10>

    def test_rejects_http_non_localhost_redirect(self):
        """Test that HTTP redirect URLs for non-localhost are rejected."""
        import pytest
    
>       with pytest.raises(ValueError, match="must use HTTPS"):
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E       AssertionError: Regex pattern did not match.
E         Expected regex: 'must use HTTPS'
E         Actual message: 'Invalid redirect_url: must be a complete URL'

tests/test_manifest.py:25: AssertionError
=========================== short test summary info ============================
FAILED tests/test_manifest.py::TestUrlValidation::test_rejects_http_non_localhost_redirect - AssertionError: Regex pattern did not match.
  Expected regex: 'must use HTTPS'
  Actual message: 'Invalid redirect_url: must be a complete URL'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 110 passed, 3 deselected in 0.89s
operator: core/ReplaceUnaryOperator_Delete_Not, occurrence: 1
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -27,7 +27,7 @@
     """
     parsed = urlparse(url)
 
-    if not parsed.scheme or not parsed.netloc:
+    if not parsed.scheme or  parsed.netloc:
         raise ValueError(f"Invalid {name}: must be a complete URL")
 
     # Allow http only for localhost (development)
........................................................................ [ 34%]
......................................F
=================================== FAILURES ===================================
__________ TestUrlValidation.test_rejects_http_non_localhost_redirect __________

self = <tests.test_manifest.TestUrlValidation object at 0x7fd20ce1ec10>

    def test_rejects_http_non_localhost_redirect(self):
        """Test that HTTP redirect URLs for non-localhost are rejected."""
        import pytest
    
        with pytest.raises(ValueError, match="must use HTTPS"):
>           create_manifest(
                redirect_url="http://example.com/setup/callback",
            )

tests/test_manifest.py:26: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/manifest.py:85: in create_manifest
    _validate_url(redirect_url, "redirect_url")
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

url = 'http://example.com/setup/callback', name = 'redirect_url'

    def _validate_url(url: str, name: str) -> None:
        """Validate that a URL is well-formed and uses HTTPS (or localhost).
    
        Args:
            url: URL to validate
            name: Name of the URL parameter for error messages
    
        Raises:
            ValueError: If URL is invalid or doesn't use HTTPS
        """
        parsed = urlparse(url)
    
        if not parsed.scheme or  parsed.netloc:
>           raise ValueError(f"Invalid {name}: must be a complete URL")
E           ValueError: Invalid redirect_url: must be a complete URL

stampbot/manifest.py:31: ValueError

During handling of the above exception, another exception occurred:

self = <tests.test_manifest.TestUrlValidation object at 0x7fd20ce1ec10>

    def test_rejects_http_non_localhost_redirect(self):
        """Test that HTTP redirect URLs for non-localhost are rejected."""
        import pytest
    
>       with pytest.raises(ValueError, match="must use HTTPS"):
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E       AssertionError: Regex pattern did not match.
E         Expected regex: 'must use HTTPS'
E         Actual message: 'Invalid redirect_url: must be a complete URL'

tests/test_manifest.py:25: AssertionError
=========================== short test summary info ============================
FAILED tests/test_manifest.py::TestUrlValidation::test_rejects_http_non_localhost_redirect - AssertionError: Regex pattern did not match.
  Expected regex: 'must use HTTPS'
  Actual message: 'Invalid redirect_url: must be a complete URL'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 110 passed, 3 deselected in 0.88s
operator: core/ReplaceUnaryOperator_Delete_Not, occurrence: 2
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -147,7 +147,7 @@
         ValueError: If code contains invalid characters
     """
     # Validate code to prevent SSRF - GitHub codes are alphanumeric
-    if not code or not code.isalnum():
+    if  code or not code.isalnum():
         raise ValueError("Invalid manifest code format")
 
     url = GITHUB_MANIFEST_CONVERSION_URL.format(code=code)
........................................................................ [ 34%]
.....................................................F
=================================== FAILURES ===================================
_________________ TestExchangeCode.test_exchange_code_success __________________

self = <tests.test_manifest.TestExchangeCode object at 0x7f6f85b47250>

    @pytest.mark.asyncio
    async def test_exchange_code_success(self):
        """Test successful code exchange."""
        import httpx
    
        mock_response_data = {
            "id": 12345,
            "pem": "-----BEGIN RSA PRIVATE KEY-----\ntest\n-----END RSA PRIVATE KEY-----",
            "webhook_secret": "test-secret",
            "slug": "my-stampbot",
            "name": "My Stampbot",
            "client_id": "client123",
            "client_secret": "secret456",
        }
    
        # Create a proper mock response with request attached
        mock_request = httpx.Request(
            "POST", "https://api.github.com/app-manifests/testcode123/conversions"
        )
        mock_response = httpx.Response(200, json=mock_response_data, request=mock_request)
    
        with patch("httpx.AsyncClient.post", new_callable=AsyncMock) as mock_post:
            mock_post.return_value = mock_response
    
>           result = await exchange_code_for_credentials("testcode123")
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_manifest.py:204: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

code = 'testcode123'

    async def exchange_code_for_credentials(code: str) -> dict[str, Any]:
        """Exchange the temporary code for app credentials.
    
        After a user creates a GitHub App from a manifest, GitHub redirects
        to the callback URL with a temporary code. This function exchanges
        that code for the actual app credentials.
    
        Args:
            code: Temporary code from GitHub callback
    
        Returns:
            Dictionary with app credentials:
            - id: App ID
            - pem: Private key (PEM format)
            - webhook_secret: Webhook secret
            - slug: App slug
            - name: App name
            - client_id: OAuth client ID
            - client_secret: OAuth client secret
    
        Raises:
            httpx.HTTPStatusError: If API call fails
            ValueError: If code contains invalid characters
        """
        # Validate code to prevent SSRF - GitHub codes are alphanumeric
        if  code or not code.isalnum():
>           raise ValueError("Invalid manifest code format")
E           ValueError: Invalid manifest code format

stampbot/manifest.py:151: ValueError
=========================== short test summary info ============================
FAILED tests/test_manifest.py::TestExchangeCode::test_exchange_code_success - ValueError: Invalid manifest code format
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 125 passed, 3 deselected in 0.90s
operator: core/ReplaceUnaryOperator_Delete_Not, occurrence: 3
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -147,7 +147,7 @@
         ValueError: If code contains invalid characters
     """
     # Validate code to prevent SSRF - GitHub codes are alphanumeric
-    if not code or not code.isalnum():
+    if not code or  code.isalnum():
         raise ValueError("Invalid manifest code format")
 
     url = GITHUB_MANIFEST_CONVERSION_URL.format(code=code)
........................................................................ [ 34%]
.....................................................F
=================================== FAILURES ===================================
_________________ TestExchangeCode.test_exchange_code_success __________________

self = <tests.test_manifest.TestExchangeCode object at 0x7f4ae2757250>

    @pytest.mark.asyncio
    async def test_exchange_code_success(self):
        """Test successful code exchange."""
        import httpx
    
        mock_response_data = {
            "id": 12345,
            "pem": "-----BEGIN RSA PRIVATE KEY-----\ntest\n-----END RSA PRIVATE KEY-----",
            "webhook_secret": "test-secret",
            "slug": "my-stampbot",
            "name": "My Stampbot",
            "client_id": "client123",
            "client_secret": "secret456",
        }
    
        # Create a proper mock response with request attached
        mock_request = httpx.Request(
            "POST", "https://api.github.com/app-manifests/testcode123/conversions"
        )
        mock_response = httpx.Response(200, json=mock_response_data, request=mock_request)
    
        with patch("httpx.AsyncClient.post", new_callable=AsyncMock) as mock_post:
            mock_post.return_value = mock_response
    
>           result = await exchange_code_for_credentials("testcode123")
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_manifest.py:204: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

code = 'testcode123'

    async def exchange_code_for_credentials(code: str) -> dict[str, Any]:
        """Exchange the temporary code for app credentials.
    
        After a user creates a GitHub App from a manifest, GitHub redirects
        to the callback URL with a temporary code. This function exchanges
        that code for the actual app credentials.
    
        Args:
            code: Temporary code from GitHub callback
    
        Returns:
            Dictionary with app credentials:
            - id: App ID
            - pem: Private key (PEM format)
            - webhook_secret: Webhook secret
            - slug: App slug
            - name: App name
            - client_id: OAuth client ID
            - client_secret: OAuth client secret
    
        Raises:
            httpx.HTTPStatusError: If API call fails
            ValueError: If code contains invalid characters
        """
        # Validate code to prevent SSRF - GitHub codes are alphanumeric
        if not code or  code.isalnum():
>           raise ValueError("Invalid manifest code format")
E           ValueError: Invalid manifest code format

stampbot/manifest.py:151: ValueError
=========================== short test summary info ============================
FAILED tests/test_manifest.py::TestExchangeCode::test_exchange_code_success - ValueError: Invalid manifest code format
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 125 passed, 3 deselected in 0.88s
operator: core/AddNot, occurrence: 0
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -27,7 +27,7 @@
     """
     parsed = urlparse(url)
 
-    if not parsed.scheme or not parsed.netloc:
+    if not not parsed.scheme or not parsed.netloc:
         raise ValueError(f"Invalid {name}: must be a complete URL")
 
     # Allow http only for localhost (development)
........................................................................ [ 34%]
......................................F
=================================== FAILURES ===================================
__________ TestUrlValidation.test_rejects_http_non_localhost_redirect __________

self = <tests.test_manifest.TestUrlValidation object at 0x7f184f88ec10>

    def test_rejects_http_non_localhost_redirect(self):
        """Test that HTTP redirect URLs for non-localhost are rejected."""
        import pytest
    
        with pytest.raises(ValueError, match="must use HTTPS"):
>           create_manifest(
                redirect_url="http://example.com/setup/callback",
            )

tests/test_manifest.py:26: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/manifest.py:85: in create_manifest
    _validate_url(redirect_url, "redirect_url")
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

url = 'http://example.com/setup/callback', name = 'redirect_url'

    def _validate_url(url: str, name: str) -> None:
        """Validate that a URL is well-formed and uses HTTPS (or localhost).
    
        Args:
            url: URL to validate
            name: Name of the URL parameter for error messages
    
        Raises:
            ValueError: If URL is invalid or doesn't use HTTPS
        """
        parsed = urlparse(url)
    
        if not not parsed.scheme or not parsed.netloc:
>           raise ValueError(f"Invalid {name}: must be a complete URL")
E           ValueError: Invalid redirect_url: must be a complete URL

stampbot/manifest.py:31: ValueError

During handling of the above exception, another exception occurred:

self = <tests.test_manifest.TestUrlValidation object at 0x7f184f88ec10>

    def test_rejects_http_non_localhost_redirect(self):
        """Test that HTTP redirect URLs for non-localhost are rejected."""
        import pytest
    
>       with pytest.raises(ValueError, match="must use HTTPS"):
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E       AssertionError: Regex pattern did not match.
E         Expected regex: 'must use HTTPS'
E         Actual message: 'Invalid redirect_url: must be a complete URL'

tests/test_manifest.py:25: AssertionError
=========================== short test summary info ============================
FAILED tests/test_manifest.py::TestUrlValidation::test_rejects_http_non_localhost_redirect - AssertionError: Regex pattern did not match.
  Expected regex: 'must use HTTPS'
  Actual message: 'Invalid redirect_url: must be a complete URL'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 110 passed, 3 deselected in 0.88s
operator: core/AddNot, occurrence: 1
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -31,7 +31,7 @@
         raise ValueError(f"Invalid {name}: must be a complete URL")
 
     # Allow http only for localhost (development)
-    if parsed.scheme == "http":
+    if not parsed.scheme == "http":
         if parsed.hostname not in ("localhost", "127.0.0.1", "::1"):
             raise ValueError(f"Invalid {name}: must use HTTPS (HTTP only allowed for localhost)")
     elif parsed.scheme != "https":
........................................................................ [ 34%]
.......................................F
=================================== FAILURES ===================================
_________________ TestUrlValidation.test_allows_http_localhost _________________

self = <tests.test_manifest.TestUrlValidation object at 0x7f611d5b9310>

    def test_allows_http_localhost(self):
        """Test that HTTP URLs for localhost are allowed."""
>       manifest = create_manifest(
            redirect_url="http://localhost:8000/setup/callback",
            webhook_url="http://localhost:8000/webhook",
        )

tests/test_manifest.py:32: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/manifest.py:85: in create_manifest
    _validate_url(redirect_url, "redirect_url")
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

url = 'http://localhost:8000/setup/callback', name = 'redirect_url'

    def _validate_url(url: str, name: str) -> None:
        """Validate that a URL is well-formed and uses HTTPS (or localhost).
    
        Args:
            url: URL to validate
            name: Name of the URL parameter for error messages
    
        Raises:
            ValueError: If URL is invalid or doesn't use HTTPS
        """
        parsed = urlparse(url)
    
        if not parsed.scheme or not parsed.netloc:
            raise ValueError(f"Invalid {name}: must be a complete URL")
    
        # Allow http only for localhost (development)
        if not parsed.scheme == "http":
            if parsed.hostname not in ("localhost", "127.0.0.1", "::1"):
                raise ValueError(f"Invalid {name}: must use HTTPS (HTTP only allowed for localhost)")
        elif parsed.scheme != "https":
>           raise ValueError(f"Invalid {name}: must use HTTPS")
E           ValueError: Invalid redirect_url: must use HTTPS

stampbot/manifest.py:38: ValueError
=========================== short test summary info ============================
FAILED tests/test_manifest.py::TestUrlValidation::test_allows_http_localhost - ValueError: Invalid redirect_url: must use HTTPS
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 111 passed, 3 deselected in 0.88s
operator: core/AddNot, occurrence: 2
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -32,7 +32,7 @@
 
     # Allow http only for localhost (development)
     if parsed.scheme == "http":
-        if parsed.hostname not in ("localhost", "127.0.0.1", "::1"):
+        if not parsed.hostname not in ("localhost", "127.0.0.1", "::1"):
             raise ValueError(f"Invalid {name}: must use HTTPS (HTTP only allowed for localhost)")
     elif parsed.scheme != "https":
         raise ValueError(f"Invalid {name}: must use HTTPS")
........................................................................ [ 34%]
......................................F
=================================== FAILURES ===================================
__________ TestUrlValidation.test_rejects_http_non_localhost_redirect __________

self = <tests.test_manifest.TestUrlValidation object at 0x7f22c853ec10>

    def test_rejects_http_non_localhost_redirect(self):
        """Test that HTTP redirect URLs for non-localhost are rejected."""
        import pytest
    
>       with pytest.raises(ValueError, match="must use HTTPS"):
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E       Failed: DID NOT RAISE <class 'ValueError'>

tests/test_manifest.py:25: Failed
=========================== short test summary info ============================
FAILED tests/test_manifest.py::TestUrlValidation::test_rejects_http_non_localhost_redirect - Failed: DID NOT RAISE <class 'ValueError'>
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 110 passed, 3 deselected in 0.88s
operator: core/AddNot, occurrence: 3
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -98,7 +98,7 @@
     }
 
     # Only include webhook URL if provided; GitHub will prompt for it otherwise
-    if webhook_url:
+    if not webhook_url:
         _validate_url(webhook_url, "webhook_url")
         manifest["hook_attributes"] = {
             "url": webhook_url,
........................................................................ [ 34%]
.......................................F
=================================== FAILURES ===================================
_________________ TestUrlValidation.test_allows_http_localhost _________________

self = <tests.test_manifest.TestUrlValidation object at 0x7fd202b41310>

    def test_allows_http_localhost(self):
        """Test that HTTP URLs for localhost are allowed."""
        manifest = create_manifest(
            redirect_url="http://localhost:8000/setup/callback",
            webhook_url="http://localhost:8000/webhook",
        )
>       assert manifest["hook_attributes"]["url"] == "http://localhost:8000/webhook"
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
E       KeyError: 'hook_attributes'

tests/test_manifest.py:36: KeyError
=========================== short test summary info ============================
FAILED tests/test_manifest.py::TestUrlValidation::test_allows_http_localhost - KeyError: 'hook_attributes'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 111 passed, 3 deselected in 0.88s
operator: core/AddNot, occurrence: 4
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -147,7 +147,7 @@
         ValueError: If code contains invalid characters
     """
     # Validate code to prevent SSRF - GitHub codes are alphanumeric
-    if not code or not code.isalnum():
+    if not not code or not code.isalnum():
         raise ValueError("Invalid manifest code format")
 
     url = GITHUB_MANIFEST_CONVERSION_URL.format(code=code)
........................................................................ [ 34%]
.....................................................F
=================================== FAILURES ===================================
_________________ TestExchangeCode.test_exchange_code_success __________________

self = <tests.test_manifest.TestExchangeCode object at 0x7ffa2bb4f250>

    @pytest.mark.asyncio
    async def test_exchange_code_success(self):
        """Test successful code exchange."""
        import httpx
    
        mock_response_data = {
            "id": 12345,
            "pem": "-----BEGIN RSA PRIVATE KEY-----\ntest\n-----END RSA PRIVATE KEY-----",
            "webhook_secret": "test-secret",
            "slug": "my-stampbot",
            "name": "My Stampbot",
            "client_id": "client123",
            "client_secret": "secret456",
        }
    
        # Create a proper mock response with request attached
        mock_request = httpx.Request(
            "POST", "https://api.github.com/app-manifests/testcode123/conversions"
        )
        mock_response = httpx.Response(200, json=mock_response_data, request=mock_request)
    
        with patch("httpx.AsyncClient.post", new_callable=AsyncMock) as mock_post:
            mock_post.return_value = mock_response
    
>           result = await exchange_code_for_credentials("testcode123")
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_manifest.py:204: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

code = 'testcode123'

    async def exchange_code_for_credentials(code: str) -> dict[str, Any]:
        """Exchange the temporary code for app credentials.
    
        After a user creates a GitHub App from a manifest, GitHub redirects
        to the callback URL with a temporary code. This function exchanges
        that code for the actual app credentials.
    
        Args:
            code: Temporary code from GitHub callback
    
        Returns:
            Dictionary with app credentials:
            - id: App ID
            - pem: Private key (PEM format)
            - webhook_secret: Webhook secret
            - slug: App slug
            - name: App name
            - client_id: OAuth client ID
            - client_secret: OAuth client secret
    
        Raises:
            httpx.HTTPStatusError: If API call fails
            ValueError: If code contains invalid characters
        """
        # Validate code to prevent SSRF - GitHub codes are alphanumeric
        if not not code or not code.isalnum():
>           raise ValueError("Invalid manifest code format")
E           ValueError: Invalid manifest code format

stampbot/manifest.py:151: ValueError
=========================== short test summary info ============================
FAILED tests/test_manifest.py::TestExchangeCode::test_exchange_code_success - ValueError: Invalid manifest code format
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 125 passed, 3 deselected in 0.89s
operator: core/ReplaceTrueWithFalse, occurrence: 0
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -102,7 +102,7 @@
         _validate_url(webhook_url, "webhook_url")
         manifest["hook_attributes"] = {
             "url": webhook_url,
-            "active": True,
+            "active": False,
         }
 
     return manifest
........................................................................ [ 34%]
............................................F
=================================== FAILURES ===================================
__________ TestCreateManifest.test_manifest_webhook_url_when_provided __________

self = <tests.test_manifest.TestCreateManifest object at 0x7ff4d11ee190>

    def test_manifest_webhook_url_when_provided(self):
        """Test manifest contains webhook URL when provided."""
        manifest = create_manifest(
            redirect_url="https://stampbot.example.com/setup/callback",
            webhook_url="https://stampbot.example.com/webhook",
        )
    
        assert manifest["hook_attributes"]["url"] == "https://stampbot.example.com/webhook"
>       assert manifest["hook_attributes"]["active"] is True
E       assert False is True

tests/test_manifest.py:92: AssertionError
=========================== short test summary info ============================
FAILED tests/test_manifest.py::TestCreateManifest::test_manifest_webhook_url_when_provided - assert False is True
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 116 passed, 3 deselected in 0.88s
operator: core/ReplaceFalseWithTrue, occurrence: 0
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -92,7 +92,7 @@
         "description": app_description,
         "url": base_url,
         "redirect_url": redirect_url,
-        "public": False,
+        "public": True,
         "default_permissions": MANIFEST_PERMISSIONS,
         "default_events": MANIFEST_EVENTS,
     }
........................................................................ [ 34%]
................................................F
=================================== FAILURES ===================================
_________________ TestCreateManifest.test_manifest_is_private __________________

self = <tests.test_manifest.TestCreateManifest object at 0x7f1fd3aa2580>

    def test_manifest_is_private(self):
        """Test manifest creates private app."""
        manifest = create_manifest(
            redirect_url="https://example.com/setup/callback",
        )
    
>       assert manifest["public"] is False
E       assert True is False

tests/test_manifest.py:124: AssertionError
=========================== short test summary info ============================
FAILED tests/test_manifest.py::TestCreateManifest::test_manifest_is_private - assert True is False
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 120 passed, 3 deselected in 0.89s
operator: core/ReplaceOrWithAnd, occurrence: 0
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -27,7 +27,7 @@
     """
     parsed = urlparse(url)
 
-    if not parsed.scheme or not parsed.netloc:
+    if not parsed.scheme and not parsed.netloc:
         raise ValueError(f"Invalid {name}: must be a complete URL")
 
     # Allow http only for localhost (development)
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceOrWithAnd, occurrence: 1
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -147,7 +147,7 @@
         ValueError: If code contains invalid characters
     """
     # Validate code to prevent SSRF - GitHub codes are alphanumeric
-    if not code or not code.isalnum():
+    if not code and not code.isalnum():
         raise ValueError("Invalid manifest code format")
 
     url = GITHUB_MANIFEST_CONVERSION_URL.format(code=code)
........................................................................ [ 34%]
........................................................F
=================================== FAILURES ===================================
________ TestExchangeCode.test_exchange_code_rejects_invalid_characters ________

self = <tests.test_manifest.TestExchangeCode object at 0x7f8b85deaea0>

    @pytest.mark.asyncio
    async def test_exchange_code_rejects_invalid_characters(self):
        """Test that code with special characters raises ValueError to prevent SSRF."""
        with pytest.raises(ValueError, match="Invalid manifest code format"):
>           await exchange_code_for_credentials("../../../etc/passwd")

tests/test_manifest.py:243: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/manifest.py:165: in exchange_code_for_credentials
    response.raise_for_status()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Response [404 Not Found]>

    def raise_for_status(self) -> Response:
        """
        Raise the `HTTPStatusError` if one occurred.
        """
        request = self._request
        if request is None:
            raise RuntimeError(
                "Cannot call `raise_for_status` as the request "
                "instance has not been set on this response."
            )
    
        if self.is_success:
            return self
    
        if self.has_redirect_location:
            message = (
                "{error_type} '{0.status_code} {0.reason_phrase}' for url '{0.url}'\n"
                "Redirect location: '{0.headers[location]}'\n"
                "For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/{0.status_code}"
            )
        else:
            message = (
                "{error_type} '{0.status_code} {0.reason_phrase}' for url '{0.url}'\n"
                "For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/{0.status_code}"
            )
    
        status_class = self.status_code // 100
        error_types = {
            1: "Informational response",
            3: "Redirect response",
            4: "Client error",
            5: "Server error",
        }
        error_type = error_types.get(status_class, "Invalid status code")
        message = message.format(self, error_type=error_type)
>       raise HTTPStatusError(message, request=request, response=self)
E       httpx.HTTPStatusError: Client error '404 Not Found' for url 'https://api.github.com/etc/passwd/conversions'
E       For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404

../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_models.py:829: HTTPStatusError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:53:53.823727Z [info     ] Exchanging manifest code for credentials _logger=<_FixedFindCallerLogger stampbot.manifest (INFO)> _name=info
2026-03-27T13:53:53.972918Z [info     ] HTTP Request: POST https://api.github.com/etc/passwd/conversions "HTTP/1.1 404 Not Found"
------------------------------ Captured log call -------------------------------
INFO     stampbot.manifest:manifest.py:155 {'event': 'Exchanging manifest code for credentials', 'level': 'info', 'timestamp': '2026-03-27T13:53:53.823727Z'}
INFO     httpx:_client.py:1740 HTTP Request: POST https://api.github.com/etc/passwd/conversions "HTTP/1.1 404 Not Found"
=========================== short test summary info ============================
FAILED tests/test_manifest.py::TestExchangeCode::test_exchange_code_rejects_invalid_characters - httpx.HTTPStatusError: Client error '404 Not Found' for url 'https://api.github.com/etc/passwd/conversions'
For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 128 passed, 3 deselected in 1.15s
operator: core/NumberReplacer, occurrence: 0
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -41,7 +41,7 @@
 # GitHub App Manifest URLs
 GITHUB_MANIFEST_URL = "https://github.com/settings/apps/new"
 GITHUB_MANIFEST_CONVERSION_URL = "https://api.github.com/app-manifests/{code}/conversions"
-GITHUB_MANIFEST_TIMEOUT_SECONDS = 10.0
+GITHUB_MANIFEST_TIMEOUT_SECONDS = 11.0
 
 # Required permissions for stampbot
 MANIFEST_PERMISSIONS = {
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/NumberReplacer, occurrence: 1
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -41,7 +41,7 @@
 # GitHub App Manifest URLs
 GITHUB_MANIFEST_URL = "https://github.com/settings/apps/new"
 GITHUB_MANIFEST_CONVERSION_URL = "https://api.github.com/app-manifests/{code}/conversions"
-GITHUB_MANIFEST_TIMEOUT_SECONDS = 10.0
+GITHUB_MANIFEST_TIMEOUT_SECONDS = 9.0
 
 # Required permissions for stampbot
 MANIFEST_PERMISSIONS = {
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/NumberReplacer, occurrence: 2
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -85,7 +85,7 @@
     _validate_url(redirect_url, "redirect_url")
 
     # Extract base URL from redirect URL
-    base_url = redirect_url.rsplit("/setup/callback", 1)[0]
+    base_url = redirect_url.rsplit("/setup/callback", 2)[0]
 
     manifest: dict[str, Any] = {
         "name": app_name,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/NumberReplacer, occurrence: 3
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -85,7 +85,7 @@
     _validate_url(redirect_url, "redirect_url")
 
     # Extract base URL from redirect URL
-    base_url = redirect_url.rsplit("/setup/callback", 1)[0]
+    base_url = redirect_url.rsplit("/setup/callback", 0)[0]
 
     manifest: dict[str, Any] = {
         "name": app_name,
........................................................................ [ 34%]
...............................................F
=================================== FAILURES ===================================
__________________ TestCreateManifest.test_manifest_base_url ___________________

self = <tests.test_manifest.TestCreateManifest object at 0x7fbac185bdf0>

    def test_manifest_base_url(self):
        """Test manifest contains correct base URL derived from redirect URL."""
        manifest = create_manifest(
            redirect_url="https://stampbot.example.com/setup/callback",
        )
    
>       assert manifest["url"] == "https://stampbot.example.com"
E       AssertionError: assert 'https://stam...etup/callback' == 'https://stampbot.example.com'
E         
E         - https://stampbot.example.com
E         + https://stampbot.example.com/setup/callback
E         ?                             +++++++++++++++

tests/test_manifest.py:116: AssertionError
=========================== short test summary info ============================
FAILED tests/test_manifest.py::TestCreateManifest::test_manifest_base_url - AssertionError: assert 'https://stam...etup/callback' == 'https://stampbot.example.com'
  
  - https://stampbot.example.com
  + https://stampbot.example.com/setup/callback
  ?                             +++++++++++++++
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 119 passed, 3 deselected in 0.89s
operator: core/NumberReplacer, occurrence: 4
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -85,7 +85,7 @@
     _validate_url(redirect_url, "redirect_url")
 
     # Extract base URL from redirect URL
-    base_url = redirect_url.rsplit("/setup/callback", 1)[0]
+    base_url = redirect_url.rsplit("/setup/callback", 1)[ 1]
 
     manifest: dict[str, Any] = {
         "name": app_name,
........................................................................ [ 34%]
...............................................F
=================================== FAILURES ===================================
__________________ TestCreateManifest.test_manifest_base_url ___________________

self = <tests.test_manifest.TestCreateManifest object at 0x7f5181907df0>

    def test_manifest_base_url(self):
        """Test manifest contains correct base URL derived from redirect URL."""
        manifest = create_manifest(
            redirect_url="https://stampbot.example.com/setup/callback",
        )
    
>       assert manifest["url"] == "https://stampbot.example.com"
E       AssertionError: assert '' == 'https://stampbot.example.com'
E         
E         - https://stampbot.example.com

tests/test_manifest.py:116: AssertionError
=========================== short test summary info ============================
FAILED tests/test_manifest.py::TestCreateManifest::test_manifest_base_url - AssertionError: assert '' == 'https://stampbot.example.com'
  
  - https://stampbot.example.com
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 119 passed, 3 deselected in 0.88s
operator: core/NumberReplacer, occurrence: 5
--- mutation diff ---
--- astampbot/manifest.py
+++ bstampbot/manifest.py
@@ -85,7 +85,7 @@
     _validate_url(redirect_url, "redirect_url")
 
     # Extract base URL from redirect URL
-    base_url = redirect_url.rsplit("/setup/callback", 1)[0]
+    base_url = redirect_url.rsplit("/setup/callback", 1)[ -1]
 
     manifest: dict[str, Any] = {
         "name": app_name,
........................................................................ [ 34%]
...............................................F
=================================== FAILURES ===================================
__________________ TestCreateManifest.test_manifest_base_url ___________________

self = <tests.test_manifest.TestCreateManifest object at 0x7fed5d2ffdf0>

    def test_manifest_base_url(self):
        """Test manifest contains correct base URL derived from redirect URL."""
        manifest = create_manifest(
            redirect_url="https://stampbot.example.com/setup/callback",
        )
    
>       assert manifest["url"] == "https://stampbot.example.com"
E       AssertionError: assert '' == 'https://stampbot.example.com'
E         
E         - https://stampbot.example.com

tests/test_manifest.py:116: AssertionError
=========================== short test summary info ============================
FAILED tests/test_manifest.py::TestCreateManifest::test_manifest_base_url - AssertionError: assert '' == 'https://stampbot.example.com'
  
  - https://stampbot.example.com
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 119 passed, 3 deselected in 0.88s
operator: core/ReplaceComparisonOperator_Eq_NotEq, occurrence: 0
--- mutation diff ---
--- astampbot/__main__.py
+++ bstampbot/__main__.py
@@ -7,7 +7,7 @@
 
 from stampbot.config import settings
 
-if __name__ == "__main__":
+if __name__ != "__main__":
     uvicorn.run(
         "stampbot.main:app",
         host=settings.host,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceComparisonOperator_Eq_Lt, occurrence: 0
--- mutation diff ---
--- astampbot/__main__.py
+++ bstampbot/__main__.py
@@ -7,7 +7,7 @@
 
 from stampbot.config import settings
 
-if __name__ == "__main__":
+if __name__ < "__main__":
     uvicorn.run(
         "stampbot.main:app",
         host=settings.host,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceComparisonOperator_Eq_LtE, occurrence: 0
--- mutation diff ---
--- astampbot/__main__.py
+++ bstampbot/__main__.py
@@ -7,7 +7,7 @@
 
 from stampbot.config import settings
 
-if __name__ == "__main__":
+if __name__ <= "__main__":
     uvicorn.run(
         "stampbot.main:app",
         host=settings.host,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceComparisonOperator_Eq_Gt, occurrence: 0
--- mutation diff ---
--- astampbot/__main__.py
+++ bstampbot/__main__.py
@@ -7,7 +7,7 @@
 
 from stampbot.config import settings
 
-if __name__ == "__main__":
+if __name__ > "__main__":
     uvicorn.run(
         "stampbot.main:app",
         host=settings.host,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceComparisonOperator_Eq_GtE, occurrence: 0
--- mutation diff ---
--- astampbot/__main__.py
+++ bstampbot/__main__.py
@@ -7,7 +7,7 @@
 
 from stampbot.config import settings
 
-if __name__ == "__main__":
+if __name__ >= "__main__":
     uvicorn.run(
         "stampbot.main:app",
         host=settings.host,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceComparisonOperator_Eq_Is, occurrence: 0
--- mutation diff ---
--- astampbot/__main__.py
+++ bstampbot/__main__.py
@@ -7,7 +7,7 @@
 
 from stampbot.config import settings
 
-if __name__ == "__main__":
+if __name__ is "__main__":
     uvicorn.run(
         "stampbot.main:app",
         host=settings.host,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/ReplaceComparisonOperator_Eq_IsNot, occurrence: 0
--- mutation diff ---
--- astampbot/__main__.py
+++ bstampbot/__main__.py
@@ -7,7 +7,7 @@
 
 from stampbot.config import settings
 
-if __name__ == "__main__":
+if __name__ is not "__main__":
     uvicorn.run(
         "stampbot.main:app",
         host=settings.host,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/AddNot, occurrence: 0
--- mutation diff ---
--- astampbot/__main__.py
+++ bstampbot/__main__.py
@@ -7,7 +7,7 @@
 
 from stampbot.config import settings
 
-if __name__ == "__main__":
+if not __name__ == "__main__":
     uvicorn.run(
         "stampbot.main:app",
         host=settings.host,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/ReplaceFalseWithTrue, occurrence: 0
--- mutation diff ---
--- astampbot/__main__.py
+++ bstampbot/__main__.py
@@ -13,6 +13,6 @@
         host=settings.host,
         port=settings.port,
         log_level=settings.log_level.lower(),
-        reload=False,
+        reload=True,
     )
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Add_Sub, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -71,7 +71,7 @@
             return False
 
         expected_signature = (
-            "sha256=" + hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
+            "sha256=" - hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
         )
 
         return hmac.compare_digest(expected_signature, signature)
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7f350afaf770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
>       response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )

tests/test_main.py:69: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:546: in post
    return super().post(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1144: in post
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:296: in webhook
    if not webhook_handler.verify_signature(body, x_hub_signature_256):
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7f350afbacf0>
payload = b'{"test": "data"}', signature = 'sha256=invalid'

    def verify_signature(self, payload: bytes, signature: str) -> bool:
        """Verify GitHub webhook signature.
    
        Args:
            payload: Request payload
            signature: X-Hub-Signature-256 header value
    
        Returns:
            True if signature is valid
        """
        if not signature:
            return False
    
        expected_signature = (
>           "sha256=" - hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
        )
E       TypeError: unsupported operand type(s) for -: 'str' and 'str'

stampbot/webhook_handler.py:74: TypeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - TypeError: unsupported operand type(s) for -: 'str' and 'str'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 1.17s
operator: core/ReplaceBinaryOperator_Add_Sub, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -451,7 +451,7 @@
             command = command_match.group(1).lower()
             add_span_attributes(span, {"chatops.command": command})
 
-            if command in repo_config.approve_commands + repo_config.unapprove_commands:
+            if command in repo_config.approve_commands - repo_config.unapprove_commands:
                 has_permission = await run_in_threadpool(
                     github_client.user_has_permission,
                     installation_id,
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f4f7932e150>
mock_github_client = <MagicMock name='github_client' id='139979313065536'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
>       result = await webhook_handler.handle_event("issue_comment", payload)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_webhook_handler.py:257: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/webhook_handler.py:119: in handle_event
    result = await self._handle_pr_comment(payload)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7f4f7932e150>
payload = {'action': 'created', 'comment': {'body': '@stampbot approve\n\nLGTM, approving this PR.', 'created_at': '2025-01-10T1...U='}, 'issue': {'assignee': None, 'assignees': [], 'body': 'This PR adds a new feature.', 'closed_at': None, ...}, ...}

    async def _handle_pr_comment(self, payload: dict[str, Any]) -> dict[str, Any]:
        """Handle PR comment events for chatops.
    
        Args:
            payload: Event payload
    
        Returns:
            Response dictionary
        """
        comment = payload.get("comment", {})
        comment_body = comment.get("body", "")
    
        # Security: limit comment length to prevent DoS
        if len(comment_body) > MAX_COMMENT_LENGTH:
            return {"status": "ignored", "message": "Comment too long"}
    
        comment_body = comment_body.lower().strip()
    
        # Check if bot is mentioned
        if "@stampbot" not in comment_body:
            return {"status": "ignored", "message": "Bot not mentioned"}
    
        # Extract PR info
        if "pull_request" in payload.get("issue", {}):
            pr_url = payload["issue"]["pull_request"]["url"]
            # Extract PR number from URL
            pr_number = int(pr_url.split("/")[-1])
        elif "pull_request" in payload:
            pr_number = payload["pull_request"]["number"]
        else:
            return {"status": "error", "message": "Not a PR comment"}
    
        repo = payload.get("repository", {})
        repo_full_name = repo.get("full_name")
        repo_default_branch = repo.get("default_branch") or "main"
        repo_owner = repo.get("owner", {})
        owner_login = repo_owner.get("login")
        owner_type = repo_owner.get("type")
        installation_id = payload.get("installation", {}).get("id")
        commenter = comment.get("user", {}).get("login", "unknown")
    
        with create_span(
            "webhook.handle_chatops",
            {
                "github.repo": repo_full_name or "unknown",
                "github.pr_number": pr_number,
                "chatops.commenter": commenter,
            },
        ) as span:
            if not all([pr_number, repo_full_name, installation_id]):
                logger.warning("Missing required fields in comment event")
                add_span_attributes(span, {"chatops.result": "missing_fields"})
                set_span_ok(span)
                return {"status": "error", "message": "Missing required fields"}
    
            # Get repository configuration
            repo_config = await self._get_repo_config(
                installation_id,
                repo_full_name,
                repo_default_branch,
                owner_login,
                owner_type,
            )
    
            if repo_config.config_error:
                add_span_attributes(
                    span,
                    {
                        "chatops.result": "config_error",
                        "chatops.config_error": repo_config.config_error,
                    },
                )
                set_span_ok(span)
                return {
                    "status": "error",
                    "message": "Invalid repository configuration",
                }
    
            if not repo_config.chatops_enabled:
                add_span_attributes(span, {"chatops.result": "disabled"})
                set_span_ok(span)
                return {"status": "ignored", "message": "Chatops not enabled"}
    
            # Parse command
            command_match = re.search(r"@stampbot\s+(\w+)", comment_body)
            if not command_match:
                chatops_commands_total.labels(command="none", status="ignored").inc()
                add_span_attributes(span, {"chatops.result": "no_command"})
                set_span_ok(span)
                return {"status": "ignored", "message": "No command found"}
    
            command = command_match.group(1).lower()
            add_span_attributes(span, {"chatops.command": command})
    
>           if command in repo_config.approve_commands - repo_config.unapprove_commands:
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E           TypeError: unsupported operand type(s) for -: 'BoxList' and 'BoxList'

stampbot/webhook_handler.py:454: TypeError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:51:42.025770Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-03-27T13:51:42.026333Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T13:51:42.025770Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:51:42.026333Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - TypeError: unsupported operand type(s) for -: 'BoxList' and 'BoxList'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.17s
operator: core/ReplaceBinaryOperator_Add_Mul, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -71,7 +71,7 @@
             return False
 
         expected_signature = (
-            "sha256=" + hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
+            "sha256=" * hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
         )
 
         return hmac.compare_digest(expected_signature, signature)
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7f87d7987770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
>       response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )

tests/test_main.py:69: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:546: in post
    return super().post(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1144: in post
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:450: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:296: in webhook
    if not webhook_handler.verify_signature(body, x_hub_signature_256):
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7f87d798ecf0>
payload = b'{"test": "data"}', signature = 'sha256=invalid'

    def verify_signature(self, payload: bytes, signature: str) -> bool:
        """Verify GitHub webhook signature.
    
        Args:
            payload: Request payload
            signature: X-Hub-Signature-256 header value
    
        Returns:
            True if signature is valid
        """
        if not signature:
            return False
    
        expected_signature = (
>           "sha256=" * hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
        )
E       TypeError: can't multiply sequence by non-int of type 'str'

stampbot/webhook_handler.py:74: TypeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - TypeError: can't multiply sequence by non-int of type 'str'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 1.18s
operator: core/ReplaceBinaryOperator_Add_Mul, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -451,7 +451,7 @@
             command = command_match.group(1).lower()
             add_span_attributes(span, {"chatops.command": command})
 
-            if command in repo_config.approve_commands + repo_config.unapprove_commands:
+            if command in repo_config.approve_commands * repo_config.unapprove_commands:
                 has_permission = await run_in_threadpool(
                     github_client.user_has_permission,
                     installation_id,
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f4881766150>
mock_github_client = <MagicMock name='github_client' id='139949386935872'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
>       result = await webhook_handler.handle_event("issue_comment", payload)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_webhook_handler.py:257: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/webhook_handler.py:119: in handle_event
    result = await self._handle_pr_comment(payload)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7f4881766150>
payload = {'action': 'created', 'comment': {'body': '@stampbot approve\n\nLGTM, approving this PR.', 'created_at': '2025-01-10T1...U='}, 'issue': {'assignee': None, 'assignees': [], 'body': 'This PR adds a new feature.', 'closed_at': None, ...}, ...}

    async def _handle_pr_comment(self, payload: dict[str, Any]) -> dict[str, Any]:
        """Handle PR comment events for chatops.
    
        Args:
            payload: Event payload
    
        Returns:
            Response dictionary
        """
        comment = payload.get("comment", {})
        comment_body = comment.get("body", "")
    
        # Security: limit comment length to prevent DoS
        if len(comment_body) > MAX_COMMENT_LENGTH:
            return {"status": "ignored", "message": "Comment too long"}
    
        comment_body = comment_body.lower().strip()
    
        # Check if bot is mentioned
        if "@stampbot" not in comment_body:
            return {"status": "ignored", "message": "Bot not mentioned"}
    
        # Extract PR info
        if "pull_request" in payload.get("issue", {}):
            pr_url = payload["issue"]["pull_request"]["url"]
            # Extract PR number from URL
            pr_number = int(pr_url.split("/")[-1])
        elif "pull_request" in payload:
            pr_number = payload["pull_request"]["number"]
        else:
            return {"status": "error", "message": "Not a PR comment"}
    
        repo = payload.get("repository", {})
        repo_full_name = repo.get("full_name")
        repo_default_branch = repo.get("default_branch") or "main"
        repo_owner = repo.get("owner", {})
        owner_login = repo_owner.get("login")
        owner_type = repo_owner.get("type")
        installation_id = payload.get("installation", {}).get("id")
        commenter = comment.get("user", {}).get("login", "unknown")
    
        with create_span(
            "webhook.handle_chatops",
            {
                "github.repo": repo_full_name or "unknown",
                "github.pr_number": pr_number,
                "chatops.commenter": commenter,
            },
        ) as span:
            if not all([pr_number, repo_full_name, installation_id]):
                logger.warning("Missing required fields in comment event")
                add_span_attributes(span, {"chatops.result": "missing_fields"})
                set_span_ok(span)
                return {"status": "error", "message": "Missing required fields"}
    
            # Get repository configuration
            repo_config = await self._get_repo_config(
                installation_id,
                repo_full_name,
                repo_default_branch,
                owner_login,
                owner_type,
            )
    
            if repo_config.config_error:
                add_span_attributes(
                    span,
                    {
                        "chatops.result": "config_error",
                        "chatops.config_error": repo_config.config_error,
                    },
                )
                set_span_ok(span)
                return {
                    "status": "error",
                    "message": "Invalid repository configuration",
                }
    
            if not repo_config.chatops_enabled:
                add_span_attributes(span, {"chatops.result": "disabled"})
                set_span_ok(span)
                return {"status": "ignored", "message": "Chatops not enabled"}
    
            # Parse command
            command_match = re.search(r"@stampbot\s+(\w+)", comment_body)
            if not command_match:
                chatops_commands_total.labels(command="none", status="ignored").inc()
                add_span_attributes(span, {"chatops.result": "no_command"})
                set_span_ok(span)
                return {"status": "ignored", "message": "No command found"}
    
            command = command_match.group(1).lower()
            add_span_attributes(span, {"chatops.command": command})
    
>           if command in repo_config.approve_commands * repo_config.unapprove_commands:
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E           TypeError: can't multiply sequence by non-int of type 'BoxList'

stampbot/webhook_handler.py:454: TypeError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:55:19.265828Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-03-27T13:55:19.266353Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T13:55:19.265828Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:55:19.266353Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - TypeError: can't multiply sequence by non-int of type 'BoxList'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.17s
operator: core/ReplaceBinaryOperator_Add_Div, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -71,7 +71,7 @@
             return False
 
         expected_signature = (
-            "sha256=" + hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
+            "sha256=" / hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
         )
 
         return hmac.compare_digest(expected_signature, signature)
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7f448f1af770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
>       response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )

tests/test_main.py:69: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:546: in post
    return super().post(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1144: in post
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:296: in webhook
    if not webhook_handler.verify_signature(body, x_hub_signature_256):
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7f448f1b6cf0>
payload = b'{"test": "data"}', signature = 'sha256=invalid'

    def verify_signature(self, payload: bytes, signature: str) -> bool:
        """Verify GitHub webhook signature.
    
        Args:
            payload: Request payload
            signature: X-Hub-Signature-256 header value
    
        Returns:
            True if signature is valid
        """
        if not signature:
            return False
    
        expected_signature = (
>           "sha256=" / hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
        )
E       TypeError: unsupported operand type(s) for /: 'str' and 'str'

stampbot/webhook_handler.py:74: TypeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - TypeError: unsupported operand type(s) for /: 'str' and 'str'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 1.20s
operator: core/ReplaceBinaryOperator_Add_Div, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -451,7 +451,7 @@
             command = command_match.group(1).lower()
             add_span_attributes(span, {"chatops.command": command})
 
-            if command in repo_config.approve_commands + repo_config.unapprove_commands:
+            if command in repo_config.approve_commands / repo_config.unapprove_commands:
                 has_permission = await run_in_threadpool(
                     github_client.user_has_permission,
                     installation_id,
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f3e7b03a150>
mock_github_client = <MagicMock name='github_client' id='139906329079360'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
>       result = await webhook_handler.handle_event("issue_comment", payload)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_webhook_handler.py:257: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/webhook_handler.py:119: in handle_event
    result = await self._handle_pr_comment(payload)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7f3e7b03a150>
payload = {'action': 'created', 'comment': {'body': '@stampbot approve\n\nLGTM, approving this PR.', 'created_at': '2025-01-10T1...U='}, 'issue': {'assignee': None, 'assignees': [], 'body': 'This PR adds a new feature.', 'closed_at': None, ...}, ...}

    async def _handle_pr_comment(self, payload: dict[str, Any]) -> dict[str, Any]:
        """Handle PR comment events for chatops.
    
        Args:
            payload: Event payload
    
        Returns:
            Response dictionary
        """
        comment = payload.get("comment", {})
        comment_body = comment.get("body", "")
    
        # Security: limit comment length to prevent DoS
        if len(comment_body) > MAX_COMMENT_LENGTH:
            return {"status": "ignored", "message": "Comment too long"}
    
        comment_body = comment_body.lower().strip()
    
        # Check if bot is mentioned
        if "@stampbot" not in comment_body:
            return {"status": "ignored", "message": "Bot not mentioned"}
    
        # Extract PR info
        if "pull_request" in payload.get("issue", {}):
            pr_url = payload["issue"]["pull_request"]["url"]
            # Extract PR number from URL
            pr_number = int(pr_url.split("/")[-1])
        elif "pull_request" in payload:
            pr_number = payload["pull_request"]["number"]
        else:
            return {"status": "error", "message": "Not a PR comment"}
    
        repo = payload.get("repository", {})
        repo_full_name = repo.get("full_name")
        repo_default_branch = repo.get("default_branch") or "main"
        repo_owner = repo.get("owner", {})
        owner_login = repo_owner.get("login")
        owner_type = repo_owner.get("type")
        installation_id = payload.get("installation", {}).get("id")
        commenter = comment.get("user", {}).get("login", "unknown")
    
        with create_span(
            "webhook.handle_chatops",
            {
                "github.repo": repo_full_name or "unknown",
                "github.pr_number": pr_number,
                "chatops.commenter": commenter,
            },
        ) as span:
            if not all([pr_number, repo_full_name, installation_id]):
                logger.warning("Missing required fields in comment event")
                add_span_attributes(span, {"chatops.result": "missing_fields"})
                set_span_ok(span)
                return {"status": "error", "message": "Missing required fields"}
    
            # Get repository configuration
            repo_config = await self._get_repo_config(
                installation_id,
                repo_full_name,
                repo_default_branch,
                owner_login,
                owner_type,
            )
    
            if repo_config.config_error:
                add_span_attributes(
                    span,
                    {
                        "chatops.result": "config_error",
                        "chatops.config_error": repo_config.config_error,
                    },
                )
                set_span_ok(span)
                return {
                    "status": "error",
                    "message": "Invalid repository configuration",
                }
    
            if not repo_config.chatops_enabled:
                add_span_attributes(span, {"chatops.result": "disabled"})
                set_span_ok(span)
                return {"status": "ignored", "message": "Chatops not enabled"}
    
            # Parse command
            command_match = re.search(r"@stampbot\s+(\w+)", comment_body)
            if not command_match:
                chatops_commands_total.labels(command="none", status="ignored").inc()
                add_span_attributes(span, {"chatops.result": "no_command"})
                set_span_ok(span)
                return {"status": "ignored", "message": "No command found"}
    
            command = command_match.group(1).lower()
            add_span_attributes(span, {"chatops.command": command})
    
>           if command in repo_config.approve_commands / repo_config.unapprove_commands:
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E           TypeError: unsupported operand type(s) for /: 'BoxList' and 'BoxList'

stampbot/webhook_handler.py:454: TypeError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:09:27.456429Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-03-27T14:09:27.456990Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T14:09:27.456429Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T14:09:27.456990Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - TypeError: unsupported operand type(s) for /: 'BoxList' and 'BoxList'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.18s
operator: core/ReplaceBinaryOperator_Add_FloorDiv, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -71,7 +71,7 @@
             return False
 
         expected_signature = (
-            "sha256=" + hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
+            "sha256=" // hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
         )
 
         return hmac.compare_digest(expected_signature, signature)
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7f2977aa3770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
>       response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )

tests/test_main.py:69: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:546: in post
    return super().post(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1144: in post
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:296: in webhook
    if not webhook_handler.verify_signature(body, x_hub_signature_256):
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7f2977ab2cf0>
payload = b'{"test": "data"}', signature = 'sha256=invalid'

    def verify_signature(self, payload: bytes, signature: str) -> bool:
        """Verify GitHub webhook signature.
    
        Args:
            payload: Request payload
            signature: X-Hub-Signature-256 header value
    
        Returns:
            True if signature is valid
        """
        if not signature:
            return False
    
        expected_signature = (
>           "sha256=" // hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
        )
E       TypeError: unsupported operand type(s) for //: 'str' and 'str'

stampbot/webhook_handler.py:74: TypeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - TypeError: unsupported operand type(s) for //: 'str' and 'str'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 1.17s
operator: core/ReplaceBinaryOperator_Add_FloorDiv, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -451,7 +451,7 @@
             command = command_match.group(1).lower()
             add_span_attributes(span, {"chatops.command": command})
 
-            if command in repo_config.approve_commands + repo_config.unapprove_commands:
+            if command in repo_config.approve_commands // repo_config.unapprove_commands:
                 has_permission = await run_in_threadpool(
                     github_client.user_has_permission,
                     installation_id,
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fb57054e150>
mock_github_client = <MagicMock name='github_client' id='140417250963008'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
>       result = await webhook_handler.handle_event("issue_comment", payload)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_webhook_handler.py:257: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/webhook_handler.py:119: in handle_event
    result = await self._handle_pr_comment(payload)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7fb57054e150>
payload = {'action': 'created', 'comment': {'body': '@stampbot approve\n\nLGTM, approving this PR.', 'created_at': '2025-01-10T1...U='}, 'issue': {'assignee': None, 'assignees': [], 'body': 'This PR adds a new feature.', 'closed_at': None, ...}, ...}

    async def _handle_pr_comment(self, payload: dict[str, Any]) -> dict[str, Any]:
        """Handle PR comment events for chatops.
    
        Args:
            payload: Event payload
    
        Returns:
            Response dictionary
        """
        comment = payload.get("comment", {})
        comment_body = comment.get("body", "")
    
        # Security: limit comment length to prevent DoS
        if len(comment_body) > MAX_COMMENT_LENGTH:
            return {"status": "ignored", "message": "Comment too long"}
    
        comment_body = comment_body.lower().strip()
    
        # Check if bot is mentioned
        if "@stampbot" not in comment_body:
            return {"status": "ignored", "message": "Bot not mentioned"}
    
        # Extract PR info
        if "pull_request" in payload.get("issue", {}):
            pr_url = payload["issue"]["pull_request"]["url"]
            # Extract PR number from URL
            pr_number = int(pr_url.split("/")[-1])
        elif "pull_request" in payload:
            pr_number = payload["pull_request"]["number"]
        else:
            return {"status": "error", "message": "Not a PR comment"}
    
        repo = payload.get("repository", {})
        repo_full_name = repo.get("full_name")
        repo_default_branch = repo.get("default_branch") or "main"
        repo_owner = repo.get("owner", {})
        owner_login = repo_owner.get("login")
        owner_type = repo_owner.get("type")
        installation_id = payload.get("installation", {}).get("id")
        commenter = comment.get("user", {}).get("login", "unknown")
    
        with create_span(
            "webhook.handle_chatops",
            {
                "github.repo": repo_full_name or "unknown",
                "github.pr_number": pr_number,
                "chatops.commenter": commenter,
            },
        ) as span:
            if not all([pr_number, repo_full_name, installation_id]):
                logger.warning("Missing required fields in comment event")
                add_span_attributes(span, {"chatops.result": "missing_fields"})
                set_span_ok(span)
                return {"status": "error", "message": "Missing required fields"}
    
            # Get repository configuration
            repo_config = await self._get_repo_config(
                installation_id,
                repo_full_name,
                repo_default_branch,
                owner_login,
                owner_type,
            )
    
            if repo_config.config_error:
                add_span_attributes(
                    span,
                    {
                        "chatops.result": "config_error",
                        "chatops.config_error": repo_config.config_error,
                    },
                )
                set_span_ok(span)
                return {
                    "status": "error",
                    "message": "Invalid repository configuration",
                }
    
            if not repo_config.chatops_enabled:
                add_span_attributes(span, {"chatops.result": "disabled"})
                set_span_ok(span)
                return {"status": "ignored", "message": "Chatops not enabled"}
    
            # Parse command
            command_match = re.search(r"@stampbot\s+(\w+)", comment_body)
            if not command_match:
                chatops_commands_total.labels(command="none", status="ignored").inc()
                add_span_attributes(span, {"chatops.result": "no_command"})
                set_span_ok(span)
                return {"status": "ignored", "message": "No command found"}
    
            command = command_match.group(1).lower()
            add_span_attributes(span, {"chatops.command": command})
    
>           if command in repo_config.approve_commands // repo_config.unapprove_commands:
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E           TypeError: unsupported operand type(s) for //: 'BoxList' and 'BoxList'

stampbot/webhook_handler.py:454: TypeError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:52:28.662209Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-03-27T13:52:28.662737Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T13:52:28.662209Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:52:28.662737Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - TypeError: unsupported operand type(s) for //: 'BoxList' and 'BoxList'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.17s
operator: core/ReplaceBinaryOperator_Add_Mod, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -71,7 +71,7 @@
             return False
 
         expected_signature = (
-            "sha256=" + hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
+            "sha256=" % hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
         )
 
         return hmac.compare_digest(expected_signature, signature)
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7f52e3fa7770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
>       response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )

tests/test_main.py:69: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:546: in post
    return super().post(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1144: in post
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:296: in webhook
    if not webhook_handler.verify_signature(body, x_hub_signature_256):
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7f52e3faecf0>
payload = b'{"test": "data"}', signature = 'sha256=invalid'

    def verify_signature(self, payload: bytes, signature: str) -> bool:
        """Verify GitHub webhook signature.
    
        Args:
            payload: Request payload
            signature: X-Hub-Signature-256 header value
    
        Returns:
            True if signature is valid
        """
        if not signature:
            return False
    
        expected_signature = (
>           "sha256=" % hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
        )
E       TypeError: not all arguments converted during string formatting

stampbot/webhook_handler.py:74: TypeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - TypeError: not all arguments converted during string formatting
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 1.17s
operator: core/ReplaceBinaryOperator_Add_Mod, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -451,7 +451,7 @@
             command = command_match.group(1).lower()
             add_span_attributes(span, {"chatops.command": command})
 
-            if command in repo_config.approve_commands + repo_config.unapprove_commands:
+            if command in repo_config.approve_commands % repo_config.unapprove_commands:
                 has_permission = await run_in_threadpool(
                     github_client.user_has_permission,
                     installation_id,
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f9209756150>
mock_github_client = <MagicMock name='github_client' id='140265201184320'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
>       result = await webhook_handler.handle_event("issue_comment", payload)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_webhook_handler.py:257: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/webhook_handler.py:119: in handle_event
    result = await self._handle_pr_comment(payload)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7f9209756150>
payload = {'action': 'created', 'comment': {'body': '@stampbot approve\n\nLGTM, approving this PR.', 'created_at': '2025-01-10T1...U='}, 'issue': {'assignee': None, 'assignees': [], 'body': 'This PR adds a new feature.', 'closed_at': None, ...}, ...}

    async def _handle_pr_comment(self, payload: dict[str, Any]) -> dict[str, Any]:
        """Handle PR comment events for chatops.
    
        Args:
            payload: Event payload
    
        Returns:
            Response dictionary
        """
        comment = payload.get("comment", {})
        comment_body = comment.get("body", "")
    
        # Security: limit comment length to prevent DoS
        if len(comment_body) > MAX_COMMENT_LENGTH:
            return {"status": "ignored", "message": "Comment too long"}
    
        comment_body = comment_body.lower().strip()
    
        # Check if bot is mentioned
        if "@stampbot" not in comment_body:
            return {"status": "ignored", "message": "Bot not mentioned"}
    
        # Extract PR info
        if "pull_request" in payload.get("issue", {}):
            pr_url = payload["issue"]["pull_request"]["url"]
            # Extract PR number from URL
            pr_number = int(pr_url.split("/")[-1])
        elif "pull_request" in payload:
            pr_number = payload["pull_request"]["number"]
        else:
            return {"status": "error", "message": "Not a PR comment"}
    
        repo = payload.get("repository", {})
        repo_full_name = repo.get("full_name")
        repo_default_branch = repo.get("default_branch") or "main"
        repo_owner = repo.get("owner", {})
        owner_login = repo_owner.get("login")
        owner_type = repo_owner.get("type")
        installation_id = payload.get("installation", {}).get("id")
        commenter = comment.get("user", {}).get("login", "unknown")
    
        with create_span(
            "webhook.handle_chatops",
            {
                "github.repo": repo_full_name or "unknown",
                "github.pr_number": pr_number,
                "chatops.commenter": commenter,
            },
        ) as span:
            if not all([pr_number, repo_full_name, installation_id]):
                logger.warning("Missing required fields in comment event")
                add_span_attributes(span, {"chatops.result": "missing_fields"})
                set_span_ok(span)
                return {"status": "error", "message": "Missing required fields"}
    
            # Get repository configuration
            repo_config = await self._get_repo_config(
                installation_id,
                repo_full_name,
                repo_default_branch,
                owner_login,
                owner_type,
            )
    
            if repo_config.config_error:
                add_span_attributes(
                    span,
                    {
                        "chatops.result": "config_error",
                        "chatops.config_error": repo_config.config_error,
                    },
                )
                set_span_ok(span)
                return {
                    "status": "error",
                    "message": "Invalid repository configuration",
                }
    
            if not repo_config.chatops_enabled:
                add_span_attributes(span, {"chatops.result": "disabled"})
                set_span_ok(span)
                return {"status": "ignored", "message": "Chatops not enabled"}
    
            # Parse command
            command_match = re.search(r"@stampbot\s+(\w+)", comment_body)
            if not command_match:
                chatops_commands_total.labels(command="none", status="ignored").inc()
                add_span_attributes(span, {"chatops.result": "no_command"})
                set_span_ok(span)
                return {"status": "ignored", "message": "No command found"}
    
            command = command_match.group(1).lower()
            add_span_attributes(span, {"chatops.command": command})
    
>           if command in repo_config.approve_commands % repo_config.unapprove_commands:
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E           TypeError: unsupported operand type(s) for %: 'BoxList' and 'BoxList'

stampbot/webhook_handler.py:454: TypeError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:53:43.192197Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-03-27T13:53:43.192728Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T13:53:43.192197Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:53:43.192728Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - TypeError: unsupported operand type(s) for %: 'BoxList' and 'BoxList'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.16s
operator: core/ReplaceBinaryOperator_Add_Pow, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -71,7 +71,7 @@
             return False
 
         expected_signature = (
-            "sha256=" + hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
+            "sha256=" ** hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
         )
 
         return hmac.compare_digest(expected_signature, signature)
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7fab6aab3770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
>       response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )

tests/test_main.py:69: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:546: in post
    return super().post(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1144: in post
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:296: in webhook
    if not webhook_handler.verify_signature(body, x_hub_signature_256):
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7fab6aac2cf0>
payload = b'{"test": "data"}', signature = 'sha256=invalid'

    def verify_signature(self, payload: bytes, signature: str) -> bool:
        """Verify GitHub webhook signature.
    
        Args:
            payload: Request payload
            signature: X-Hub-Signature-256 header value
    
        Returns:
            True if signature is valid
        """
        if not signature:
            return False
    
        expected_signature = (
>           "sha256=" ** hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
        )
E       TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'str'

stampbot/webhook_handler.py:74: TypeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'str'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 1.25s
operator: core/ReplaceBinaryOperator_Add_Pow, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -451,7 +451,7 @@
             command = command_match.group(1).lower()
             add_span_attributes(span, {"chatops.command": command})
 
-            if command in repo_config.approve_commands + repo_config.unapprove_commands:
+            if command in repo_config.approve_commands ** repo_config.unapprove_commands:
                 has_permission = await run_in_threadpool(
                     github_client.user_has_permission,
                     installation_id,
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f63b324a150>
mock_github_client = <MagicMock name='github_client' id='140066184556096'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
>       result = await webhook_handler.handle_event("issue_comment", payload)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_webhook_handler.py:257: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/webhook_handler.py:119: in handle_event
    result = await self._handle_pr_comment(payload)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7f63b324a150>
payload = {'action': 'created', 'comment': {'body': '@stampbot approve\n\nLGTM, approving this PR.', 'created_at': '2025-01-10T1...U='}, 'issue': {'assignee': None, 'assignees': [], 'body': 'This PR adds a new feature.', 'closed_at': None, ...}, ...}

    async def _handle_pr_comment(self, payload: dict[str, Any]) -> dict[str, Any]:
        """Handle PR comment events for chatops.
    
        Args:
            payload: Event payload
    
        Returns:
            Response dictionary
        """
        comment = payload.get("comment", {})
        comment_body = comment.get("body", "")
    
        # Security: limit comment length to prevent DoS
        if len(comment_body) > MAX_COMMENT_LENGTH:
            return {"status": "ignored", "message": "Comment too long"}
    
        comment_body = comment_body.lower().strip()
    
        # Check if bot is mentioned
        if "@stampbot" not in comment_body:
            return {"status": "ignored", "message": "Bot not mentioned"}
    
        # Extract PR info
        if "pull_request" in payload.get("issue", {}):
            pr_url = payload["issue"]["pull_request"]["url"]
            # Extract PR number from URL
            pr_number = int(pr_url.split("/")[-1])
        elif "pull_request" in payload:
            pr_number = payload["pull_request"]["number"]
        else:
            return {"status": "error", "message": "Not a PR comment"}
    
        repo = payload.get("repository", {})
        repo_full_name = repo.get("full_name")
        repo_default_branch = repo.get("default_branch") or "main"
        repo_owner = repo.get("owner", {})
        owner_login = repo_owner.get("login")
        owner_type = repo_owner.get("type")
        installation_id = payload.get("installation", {}).get("id")
        commenter = comment.get("user", {}).get("login", "unknown")
    
        with create_span(
            "webhook.handle_chatops",
            {
                "github.repo": repo_full_name or "unknown",
                "github.pr_number": pr_number,
                "chatops.commenter": commenter,
            },
        ) as span:
            if not all([pr_number, repo_full_name, installation_id]):
                logger.warning("Missing required fields in comment event")
                add_span_attributes(span, {"chatops.result": "missing_fields"})
                set_span_ok(span)
                return {"status": "error", "message": "Missing required fields"}
    
            # Get repository configuration
            repo_config = await self._get_repo_config(
                installation_id,
                repo_full_name,
                repo_default_branch,
                owner_login,
                owner_type,
            )
    
            if repo_config.config_error:
                add_span_attributes(
                    span,
                    {
                        "chatops.result": "config_error",
                        "chatops.config_error": repo_config.config_error,
                    },
                )
                set_span_ok(span)
                return {
                    "status": "error",
                    "message": "Invalid repository configuration",
                }
    
            if not repo_config.chatops_enabled:
                add_span_attributes(span, {"chatops.result": "disabled"})
                set_span_ok(span)
                return {"status": "ignored", "message": "Chatops not enabled"}
    
            # Parse command
            command_match = re.search(r"@stampbot\s+(\w+)", comment_body)
            if not command_match:
                chatops_commands_total.labels(command="none", status="ignored").inc()
                add_span_attributes(span, {"chatops.result": "no_command"})
                set_span_ok(span)
                return {"status": "ignored", "message": "No command found"}
    
            command = command_match.group(1).lower()
            add_span_attributes(span, {"chatops.command": command})
    
>           if command in repo_config.approve_commands ** repo_config.unapprove_commands:
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E           TypeError: unsupported operand type(s) for ** or pow(): 'BoxList' and 'BoxList'

stampbot/webhook_handler.py:454: TypeError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:12:26.182993Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-03-27T14:12:26.183526Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T14:12:26.182993Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T14:12:26.183526Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - TypeError: unsupported operand type(s) for ** or pow(): 'BoxList' and 'BoxList'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.17s
operator: core/ReplaceBinaryOperator_Add_RShift, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -71,7 +71,7 @@
             return False
 
         expected_signature = (
-            "sha256=" + hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
+            "sha256=" >> hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
         )
 
         return hmac.compare_digest(expected_signature, signature)
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7f615eaa7770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
>       response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )

tests/test_main.py:69: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:546: in post
    return super().post(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1144: in post
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:450: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:296: in webhook
    if not webhook_handler.verify_signature(body, x_hub_signature_256):
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7f615eab2cf0>
payload = b'{"test": "data"}', signature = 'sha256=invalid'

    def verify_signature(self, payload: bytes, signature: str) -> bool:
        """Verify GitHub webhook signature.
    
        Args:
            payload: Request payload
            signature: X-Hub-Signature-256 header value
    
        Returns:
            True if signature is valid
        """
        if not signature:
            return False
    
        expected_signature = (
>           "sha256=" >> hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
        )
E       TypeError: unsupported operand type(s) for >>: 'str' and 'str'

stampbot/webhook_handler.py:74: TypeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - TypeError: unsupported operand type(s) for >>: 'str' and 'str'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 1.20s
operator: core/ReplaceBinaryOperator_Add_RShift, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -451,7 +451,7 @@
             command = command_match.group(1).lower()
             add_span_attributes(span, {"chatops.command": command})
 
-            if command in repo_config.approve_commands + repo_config.unapprove_commands:
+            if command in repo_config.approve_commands >> repo_config.unapprove_commands:
                 has_permission = await run_in_threadpool(
                     github_client.user_has_permission,
                     installation_id,
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f93b6f5e150>
mock_github_client = <MagicMock name='github_client' id='140272407031360'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
>       result = await webhook_handler.handle_event("issue_comment", payload)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_webhook_handler.py:257: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/webhook_handler.py:119: in handle_event
    result = await self._handle_pr_comment(payload)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7f93b6f5e150>
payload = {'action': 'created', 'comment': {'body': '@stampbot approve\n\nLGTM, approving this PR.', 'created_at': '2025-01-10T1...U='}, 'issue': {'assignee': None, 'assignees': [], 'body': 'This PR adds a new feature.', 'closed_at': None, ...}, ...}

    async def _handle_pr_comment(self, payload: dict[str, Any]) -> dict[str, Any]:
        """Handle PR comment events for chatops.
    
        Args:
            payload: Event payload
    
        Returns:
            Response dictionary
        """
        comment = payload.get("comment", {})
        comment_body = comment.get("body", "")
    
        # Security: limit comment length to prevent DoS
        if len(comment_body) > MAX_COMMENT_LENGTH:
            return {"status": "ignored", "message": "Comment too long"}
    
        comment_body = comment_body.lower().strip()
    
        # Check if bot is mentioned
        if "@stampbot" not in comment_body:
            return {"status": "ignored", "message": "Bot not mentioned"}
    
        # Extract PR info
        if "pull_request" in payload.get("issue", {}):
            pr_url = payload["issue"]["pull_request"]["url"]
            # Extract PR number from URL
            pr_number = int(pr_url.split("/")[-1])
        elif "pull_request" in payload:
            pr_number = payload["pull_request"]["number"]
        else:
            return {"status": "error", "message": "Not a PR comment"}
    
        repo = payload.get("repository", {})
        repo_full_name = repo.get("full_name")
        repo_default_branch = repo.get("default_branch") or "main"
        repo_owner = repo.get("owner", {})
        owner_login = repo_owner.get("login")
        owner_type = repo_owner.get("type")
        installation_id = payload.get("installation", {}).get("id")
        commenter = comment.get("user", {}).get("login", "unknown")
    
        with create_span(
            "webhook.handle_chatops",
            {
                "github.repo": repo_full_name or "unknown",
                "github.pr_number": pr_number,
                "chatops.commenter": commenter,
            },
        ) as span:
            if not all([pr_number, repo_full_name, installation_id]):
                logger.warning("Missing required fields in comment event")
                add_span_attributes(span, {"chatops.result": "missing_fields"})
                set_span_ok(span)
                return {"status": "error", "message": "Missing required fields"}
    
            # Get repository configuration
            repo_config = await self._get_repo_config(
                installation_id,
                repo_full_name,
                repo_default_branch,
                owner_login,
                owner_type,
            )
    
            if repo_config.config_error:
                add_span_attributes(
                    span,
                    {
                        "chatops.result": "config_error",
                        "chatops.config_error": repo_config.config_error,
                    },
                )
                set_span_ok(span)
                return {
                    "status": "error",
                    "message": "Invalid repository configuration",
                }
    
            if not repo_config.chatops_enabled:
                add_span_attributes(span, {"chatops.result": "disabled"})
                set_span_ok(span)
                return {"status": "ignored", "message": "Chatops not enabled"}
    
            # Parse command
            command_match = re.search(r"@stampbot\s+(\w+)", comment_body)
            if not command_match:
                chatops_commands_total.labels(command="none", status="ignored").inc()
                add_span_attributes(span, {"chatops.result": "no_command"})
                set_span_ok(span)
                return {"status": "ignored", "message": "No command found"}
    
            command = command_match.group(1).lower()
            add_span_attributes(span, {"chatops.command": command})
    
>           if command in repo_config.approve_commands >> repo_config.unapprove_commands:
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E           TypeError: unsupported operand type(s) for >>: 'BoxList' and 'BoxList'

stampbot/webhook_handler.py:454: TypeError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:01:03.268927Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-03-27T14:01:03.269459Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T14:01:03.268927Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T14:01:03.269459Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - TypeError: unsupported operand type(s) for >>: 'BoxList' and 'BoxList'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.16s
operator: core/ReplaceBinaryOperator_Add_LShift, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -71,7 +71,7 @@
             return False
 
         expected_signature = (
-            "sha256=" + hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
+            "sha256=" << hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
         )
 
         return hmac.compare_digest(expected_signature, signature)
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7f35c95ab770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
>       response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )

tests/test_main.py:69: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:546: in post
    return super().post(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1144: in post
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:296: in webhook
    if not webhook_handler.verify_signature(body, x_hub_signature_256):
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7f35c95b6cf0>
payload = b'{"test": "data"}', signature = 'sha256=invalid'

    def verify_signature(self, payload: bytes, signature: str) -> bool:
        """Verify GitHub webhook signature.
    
        Args:
            payload: Request payload
            signature: X-Hub-Signature-256 header value
    
        Returns:
            True if signature is valid
        """
        if not signature:
            return False
    
        expected_signature = (
>           "sha256=" << hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
        )
E       TypeError: unsupported operand type(s) for <<: 'str' and 'str'

stampbot/webhook_handler.py:74: TypeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - TypeError: unsupported operand type(s) for <<: 'str' and 'str'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 1.17s
operator: core/ReplaceBinaryOperator_Add_LShift, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -451,7 +451,7 @@
             command = command_match.group(1).lower()
             add_span_attributes(span, {"chatops.command": command})
 
-            if command in repo_config.approve_commands + repo_config.unapprove_commands:
+            if command in repo_config.approve_commands << repo_config.unapprove_commands:
                 has_permission = await run_in_threadpool(
                     github_client.user_has_permission,
                     installation_id,
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fc76d926620>
mock_github_client = <MagicMock name='github_client' id='140494502800960'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
>       result = await webhook_handler.handle_event("issue_comment", payload)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_webhook_handler.py:257: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/webhook_handler.py:119: in handle_event
    result = await self._handle_pr_comment(payload)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7fc76d926620>
payload = {'action': 'created', 'comment': {'body': '@stampbot approve\n\nLGTM, approving this PR.', 'created_at': '2025-01-10T1...U='}, 'issue': {'assignee': None, 'assignees': [], 'body': 'This PR adds a new feature.', 'closed_at': None, ...}, ...}

    async def _handle_pr_comment(self, payload: dict[str, Any]) -> dict[str, Any]:
        """Handle PR comment events for chatops.
    
        Args:
            payload: Event payload
    
        Returns:
            Response dictionary
        """
        comment = payload.get("comment", {})
        comment_body = comment.get("body", "")
    
        # Security: limit comment length to prevent DoS
        if len(comment_body) > MAX_COMMENT_LENGTH:
            return {"status": "ignored", "message": "Comment too long"}
    
        comment_body = comment_body.lower().strip()
    
        # Check if bot is mentioned
        if "@stampbot" not in comment_body:
            return {"status": "ignored", "message": "Bot not mentioned"}
    
        # Extract PR info
        if "pull_request" in payload.get("issue", {}):
            pr_url = payload["issue"]["pull_request"]["url"]
            # Extract PR number from URL
            pr_number = int(pr_url.split("/")[-1])
        elif "pull_request" in payload:
            pr_number = payload["pull_request"]["number"]
        else:
            return {"status": "error", "message": "Not a PR comment"}
    
        repo = payload.get("repository", {})
        repo_full_name = repo.get("full_name")
        repo_default_branch = repo.get("default_branch") or "main"
        repo_owner = repo.get("owner", {})
        owner_login = repo_owner.get("login")
        owner_type = repo_owner.get("type")
        installation_id = payload.get("installation", {}).get("id")
        commenter = comment.get("user", {}).get("login", "unknown")
    
        with create_span(
            "webhook.handle_chatops",
            {
                "github.repo": repo_full_name or "unknown",
                "github.pr_number": pr_number,
                "chatops.commenter": commenter,
            },
        ) as span:
            if not all([pr_number, repo_full_name, installation_id]):
                logger.warning("Missing required fields in comment event")
                add_span_attributes(span, {"chatops.result": "missing_fields"})
                set_span_ok(span)
                return {"status": "error", "message": "Missing required fields"}
    
            # Get repository configuration
            repo_config = await self._get_repo_config(
                installation_id,
                repo_full_name,
                repo_default_branch,
                owner_login,
                owner_type,
            )
    
            if repo_config.config_error:
                add_span_attributes(
                    span,
                    {
                        "chatops.result": "config_error",
                        "chatops.config_error": repo_config.config_error,
                    },
                )
                set_span_ok(span)
                return {
                    "status": "error",
                    "message": "Invalid repository configuration",
                }
    
            if not repo_config.chatops_enabled:
                add_span_attributes(span, {"chatops.result": "disabled"})
                set_span_ok(span)
                return {"status": "ignored", "message": "Chatops not enabled"}
    
            # Parse command
            command_match = re.search(r"@stampbot\s+(\w+)", comment_body)
            if not command_match:
                chatops_commands_total.labels(command="none", status="ignored").inc()
                add_span_attributes(span, {"chatops.result": "no_command"})
                set_span_ok(span)
                return {"status": "ignored", "message": "No command found"}
    
            command = command_match.group(1).lower()
            add_span_attributes(span, {"chatops.command": command})
    
>           if command in repo_config.approve_commands << repo_config.unapprove_commands:
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E           TypeError: unsupported operand type(s) for <<: 'BoxList' and 'BoxList'

stampbot/webhook_handler.py:454: TypeError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:32:58.333359Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-03-27T13:32:58.333914Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T13:32:58.333359Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:32:58.333914Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - TypeError: unsupported operand type(s) for <<: 'BoxList' and 'BoxList'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.18s
operator: core/ReplaceBinaryOperator_Add_BitOr, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -71,7 +71,7 @@
             return False
 
         expected_signature = (
-            "sha256=" + hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
+            "sha256=" | hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
         )
 
         return hmac.compare_digest(expected_signature, signature)
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7fad1859b770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
>       response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )

tests/test_main.py:69: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:546: in post
    return super().post(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1144: in post
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:450: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:296: in webhook
    if not webhook_handler.verify_signature(body, x_hub_signature_256):
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7fad185a2cf0>
payload = b'{"test": "data"}', signature = 'sha256=invalid'

    def verify_signature(self, payload: bytes, signature: str) -> bool:
        """Verify GitHub webhook signature.
    
        Args:
            payload: Request payload
            signature: X-Hub-Signature-256 header value
    
        Returns:
            True if signature is valid
        """
        if not signature:
            return False
    
        expected_signature = (
>           "sha256=" | hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
        )
E       TypeError: unsupported operand type(s) for |: 'str' and 'str'

stampbot/webhook_handler.py:74: TypeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - TypeError: unsupported operand type(s) for |: 'str' and 'str'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 1.18s
operator: core/ReplaceBinaryOperator_Add_BitOr, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -451,7 +451,7 @@
             command = command_match.group(1).lower()
             add_span_attributes(span, {"chatops.command": command})
 
-            if command in repo_config.approve_commands + repo_config.unapprove_commands:
+            if command in repo_config.approve_commands | repo_config.unapprove_commands:
                 has_permission = await run_in_threadpool(
                     github_client.user_has_permission,
                     installation_id,
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f0bd5c1f800>
mock_github_client = <MagicMock name='github_client' id='139688796892736'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
>       result = await webhook_handler.handle_event("issue_comment", payload)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_webhook_handler.py:257: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/webhook_handler.py:119: in handle_event
    result = await self._handle_pr_comment(payload)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7f0bd5c1f800>
payload = {'action': 'created', 'comment': {'body': '@stampbot approve\n\nLGTM, approving this PR.', 'created_at': '2025-01-10T1...U='}, 'issue': {'assignee': None, 'assignees': [], 'body': 'This PR adds a new feature.', 'closed_at': None, ...}, ...}

    async def _handle_pr_comment(self, payload: dict[str, Any]) -> dict[str, Any]:
        """Handle PR comment events for chatops.
    
        Args:
            payload: Event payload
    
        Returns:
            Response dictionary
        """
        comment = payload.get("comment", {})
        comment_body = comment.get("body", "")
    
        # Security: limit comment length to prevent DoS
        if len(comment_body) > MAX_COMMENT_LENGTH:
            return {"status": "ignored", "message": "Comment too long"}
    
        comment_body = comment_body.lower().strip()
    
        # Check if bot is mentioned
        if "@stampbot" not in comment_body:
            return {"status": "ignored", "message": "Bot not mentioned"}
    
        # Extract PR info
        if "pull_request" in payload.get("issue", {}):
            pr_url = payload["issue"]["pull_request"]["url"]
            # Extract PR number from URL
            pr_number = int(pr_url.split("/")[-1])
        elif "pull_request" in payload:
            pr_number = payload["pull_request"]["number"]
        else:
            return {"status": "error", "message": "Not a PR comment"}
    
        repo = payload.get("repository", {})
        repo_full_name = repo.get("full_name")
        repo_default_branch = repo.get("default_branch") or "main"
        repo_owner = repo.get("owner", {})
        owner_login = repo_owner.get("login")
        owner_type = repo_owner.get("type")
        installation_id = payload.get("installation", {}).get("id")
        commenter = comment.get("user", {}).get("login", "unknown")
    
        with create_span(
            "webhook.handle_chatops",
            {
                "github.repo": repo_full_name or "unknown",
                "github.pr_number": pr_number,
                "chatops.commenter": commenter,
            },
        ) as span:
            if not all([pr_number, repo_full_name, installation_id]):
                logger.warning("Missing required fields in comment event")
                add_span_attributes(span, {"chatops.result": "missing_fields"})
                set_span_ok(span)
                return {"status": "error", "message": "Missing required fields"}
    
            # Get repository configuration
            repo_config = await self._get_repo_config(
                installation_id,
                repo_full_name,
                repo_default_branch,
                owner_login,
                owner_type,
            )
    
            if repo_config.config_error:
                add_span_attributes(
                    span,
                    {
                        "chatops.result": "config_error",
                        "chatops.config_error": repo_config.config_error,
                    },
                )
                set_span_ok(span)
                return {
                    "status": "error",
                    "message": "Invalid repository configuration",
                }
    
            if not repo_config.chatops_enabled:
                add_span_attributes(span, {"chatops.result": "disabled"})
                set_span_ok(span)
                return {"status": "ignored", "message": "Chatops not enabled"}
    
            # Parse command
            command_match = re.search(r"@stampbot\s+(\w+)", comment_body)
            if not command_match:
                chatops_commands_total.labels(command="none", status="ignored").inc()
                add_span_attributes(span, {"chatops.result": "no_command"})
                set_span_ok(span)
                return {"status": "ignored", "message": "No command found"}
    
            command = command_match.group(1).lower()
            add_span_attributes(span, {"chatops.command": command})
    
>           if command in repo_config.approve_commands | repo_config.unapprove_commands:
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E           TypeError: unsupported operand type(s) for |: 'BoxList' and 'BoxList'

stampbot/webhook_handler.py:454: TypeError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:53:18.327361Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-03-27T13:53:18.327914Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T13:53:18.327361Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:53:18.327914Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - TypeError: unsupported operand type(s) for |: 'BoxList' and 'BoxList'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.16s
operator: core/ReplaceBinaryOperator_Add_BitAnd, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -71,7 +71,7 @@
             return False
 
         expected_signature = (
-            "sha256=" + hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
+            "sha256=" & hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
         )
 
         return hmac.compare_digest(expected_signature, signature)
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7f2d52da7770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
>       response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )

tests/test_main.py:69: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:546: in post
    return super().post(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1144: in post
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:296: in webhook
    if not webhook_handler.verify_signature(body, x_hub_signature_256):
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7f2d52db2cf0>
payload = b'{"test": "data"}', signature = 'sha256=invalid'

    def verify_signature(self, payload: bytes, signature: str) -> bool:
        """Verify GitHub webhook signature.
    
        Args:
            payload: Request payload
            signature: X-Hub-Signature-256 header value
    
        Returns:
            True if signature is valid
        """
        if not signature:
            return False
    
        expected_signature = (
>           "sha256=" & hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
        )
E       TypeError: unsupported operand type(s) for &: 'str' and 'str'

stampbot/webhook_handler.py:74: TypeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - TypeError: unsupported operand type(s) for &: 'str' and 'str'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 1.17s
operator: core/ReplaceBinaryOperator_Add_BitAnd, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -451,7 +451,7 @@
             command = command_match.group(1).lower()
             add_span_attributes(span, {"chatops.command": command})
 
-            if command in repo_config.approve_commands + repo_config.unapprove_commands:
+            if command in repo_config.approve_commands & repo_config.unapprove_commands:
                 has_permission = await run_in_threadpool(
                     github_client.user_has_permission,
                     installation_id,
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fb66f256150>
mock_github_client = <MagicMock name='github_client' id='140421526040128'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
>       result = await webhook_handler.handle_event("issue_comment", payload)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_webhook_handler.py:257: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/webhook_handler.py:119: in handle_event
    result = await self._handle_pr_comment(payload)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7fb66f256150>
payload = {'action': 'created', 'comment': {'body': '@stampbot approve\n\nLGTM, approving this PR.', 'created_at': '2025-01-10T1...U='}, 'issue': {'assignee': None, 'assignees': [], 'body': 'This PR adds a new feature.', 'closed_at': None, ...}, ...}

    async def _handle_pr_comment(self, payload: dict[str, Any]) -> dict[str, Any]:
        """Handle PR comment events for chatops.
    
        Args:
            payload: Event payload
    
        Returns:
            Response dictionary
        """
        comment = payload.get("comment", {})
        comment_body = comment.get("body", "")
    
        # Security: limit comment length to prevent DoS
        if len(comment_body) > MAX_COMMENT_LENGTH:
            return {"status": "ignored", "message": "Comment too long"}
    
        comment_body = comment_body.lower().strip()
    
        # Check if bot is mentioned
        if "@stampbot" not in comment_body:
            return {"status": "ignored", "message": "Bot not mentioned"}
    
        # Extract PR info
        if "pull_request" in payload.get("issue", {}):
            pr_url = payload["issue"]["pull_request"]["url"]
            # Extract PR number from URL
            pr_number = int(pr_url.split("/")[-1])
        elif "pull_request" in payload:
            pr_number = payload["pull_request"]["number"]
        else:
            return {"status": "error", "message": "Not a PR comment"}
    
        repo = payload.get("repository", {})
        repo_full_name = repo.get("full_name")
        repo_default_branch = repo.get("default_branch") or "main"
        repo_owner = repo.get("owner", {})
        owner_login = repo_owner.get("login")
        owner_type = repo_owner.get("type")
        installation_id = payload.get("installation", {}).get("id")
        commenter = comment.get("user", {}).get("login", "unknown")
    
        with create_span(
            "webhook.handle_chatops",
            {
                "github.repo": repo_full_name or "unknown",
                "github.pr_number": pr_number,
                "chatops.commenter": commenter,
            },
        ) as span:
            if not all([pr_number, repo_full_name, installation_id]):
                logger.warning("Missing required fields in comment event")
                add_span_attributes(span, {"chatops.result": "missing_fields"})
                set_span_ok(span)
                return {"status": "error", "message": "Missing required fields"}
    
            # Get repository configuration
            repo_config = await self._get_repo_config(
                installation_id,
                repo_full_name,
                repo_default_branch,
                owner_login,
                owner_type,
            )
    
            if repo_config.config_error:
                add_span_attributes(
                    span,
                    {
                        "chatops.result": "config_error",
                        "chatops.config_error": repo_config.config_error,
                    },
                )
                set_span_ok(span)
                return {
                    "status": "error",
                    "message": "Invalid repository configuration",
                }
    
            if not repo_config.chatops_enabled:
                add_span_attributes(span, {"chatops.result": "disabled"})
                set_span_ok(span)
                return {"status": "ignored", "message": "Chatops not enabled"}
    
            # Parse command
            command_match = re.search(r"@stampbot\s+(\w+)", comment_body)
            if not command_match:
                chatops_commands_total.labels(command="none", status="ignored").inc()
                add_span_attributes(span, {"chatops.result": "no_command"})
                set_span_ok(span)
                return {"status": "ignored", "message": "No command found"}
    
            command = command_match.group(1).lower()
            add_span_attributes(span, {"chatops.command": command})
    
>           if command in repo_config.approve_commands & repo_config.unapprove_commands:
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E           TypeError: unsupported operand type(s) for &: 'BoxList' and 'BoxList'

stampbot/webhook_handler.py:454: TypeError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:38:24.401445Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-03-27T13:38:24.402002Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T13:38:24.401445Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:38:24.402002Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - TypeError: unsupported operand type(s) for &: 'BoxList' and 'BoxList'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.17s
operator: core/ReplaceBinaryOperator_Add_BitXor, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -71,7 +71,7 @@
             return False
 
         expected_signature = (
-            "sha256=" + hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
+            "sha256=" ^ hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
         )
 
         return hmac.compare_digest(expected_signature, signature)
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7fa32e9ab770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
>       response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )

tests/test_main.py:69: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:546: in post
    return super().post(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1144: in post
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:296: in webhook
    if not webhook_handler.verify_signature(body, x_hub_signature_256):
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7fa32e9b2cf0>
payload = b'{"test": "data"}', signature = 'sha256=invalid'

    def verify_signature(self, payload: bytes, signature: str) -> bool:
        """Verify GitHub webhook signature.
    
        Args:
            payload: Request payload
            signature: X-Hub-Signature-256 header value
    
        Returns:
            True if signature is valid
        """
        if not signature:
            return False
    
        expected_signature = (
>           "sha256=" ^ hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
        )
E       TypeError: unsupported operand type(s) for ^: 'str' and 'str'

stampbot/webhook_handler.py:74: TypeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - TypeError: unsupported operand type(s) for ^: 'str' and 'str'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 1.17s
operator: core/ReplaceBinaryOperator_Add_BitXor, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -451,7 +451,7 @@
             command = command_match.group(1).lower()
             add_span_attributes(span, {"chatops.command": command})
 
-            if command in repo_config.approve_commands + repo_config.unapprove_commands:
+            if command in repo_config.approve_commands ^ repo_config.unapprove_commands:
                 has_permission = await run_in_threadpool(
                     github_client.user_has_permission,
                     installation_id,
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fa059a1b070>
mock_github_client = <MagicMock name='github_client' id='140326664531520'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
>       result = await webhook_handler.handle_event("issue_comment", payload)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_webhook_handler.py:257: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/webhook_handler.py:119: in handle_event
    result = await self._handle_pr_comment(payload)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7fa059a1b070>
payload = {'action': 'created', 'comment': {'body': '@stampbot approve\n\nLGTM, approving this PR.', 'created_at': '2025-01-10T1...U='}, 'issue': {'assignee': None, 'assignees': [], 'body': 'This PR adds a new feature.', 'closed_at': None, ...}, ...}

    async def _handle_pr_comment(self, payload: dict[str, Any]) -> dict[str, Any]:
        """Handle PR comment events for chatops.
    
        Args:
            payload: Event payload
    
        Returns:
            Response dictionary
        """
        comment = payload.get("comment", {})
        comment_body = comment.get("body", "")
    
        # Security: limit comment length to prevent DoS
        if len(comment_body) > MAX_COMMENT_LENGTH:
            return {"status": "ignored", "message": "Comment too long"}
    
        comment_body = comment_body.lower().strip()
    
        # Check if bot is mentioned
        if "@stampbot" not in comment_body:
            return {"status": "ignored", "message": "Bot not mentioned"}
    
        # Extract PR info
        if "pull_request" in payload.get("issue", {}):
            pr_url = payload["issue"]["pull_request"]["url"]
            # Extract PR number from URL
            pr_number = int(pr_url.split("/")[-1])
        elif "pull_request" in payload:
            pr_number = payload["pull_request"]["number"]
        else:
            return {"status": "error", "message": "Not a PR comment"}
    
        repo = payload.get("repository", {})
        repo_full_name = repo.get("full_name")
        repo_default_branch = repo.get("default_branch") or "main"
        repo_owner = repo.get("owner", {})
        owner_login = repo_owner.get("login")
        owner_type = repo_owner.get("type")
        installation_id = payload.get("installation", {}).get("id")
        commenter = comment.get("user", {}).get("login", "unknown")
    
        with create_span(
            "webhook.handle_chatops",
            {
                "github.repo": repo_full_name or "unknown",
                "github.pr_number": pr_number,
                "chatops.commenter": commenter,
            },
        ) as span:
            if not all([pr_number, repo_full_name, installation_id]):
                logger.warning("Missing required fields in comment event")
                add_span_attributes(span, {"chatops.result": "missing_fields"})
                set_span_ok(span)
                return {"status": "error", "message": "Missing required fields"}
    
            # Get repository configuration
            repo_config = await self._get_repo_config(
                installation_id,
                repo_full_name,
                repo_default_branch,
                owner_login,
                owner_type,
            )
    
            if repo_config.config_error:
                add_span_attributes(
                    span,
                    {
                        "chatops.result": "config_error",
                        "chatops.config_error": repo_config.config_error,
                    },
                )
                set_span_ok(span)
                return {
                    "status": "error",
                    "message": "Invalid repository configuration",
                }
    
            if not repo_config.chatops_enabled:
                add_span_attributes(span, {"chatops.result": "disabled"})
                set_span_ok(span)
                return {"status": "ignored", "message": "Chatops not enabled"}
    
            # Parse command
            command_match = re.search(r"@stampbot\s+(\w+)", comment_body)
            if not command_match:
                chatops_commands_total.labels(command="none", status="ignored").inc()
                add_span_attributes(span, {"chatops.result": "no_command"})
                set_span_ok(span)
                return {"status": "ignored", "message": "No command found"}
    
            command = command_match.group(1).lower()
            add_span_attributes(span, {"chatops.command": command})
    
>           if command in repo_config.approve_commands ^ repo_config.unapprove_commands:
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E           TypeError: unsupported operand type(s) for ^: 'BoxList' and 'BoxList'

stampbot/webhook_handler.py:454: TypeError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:05:33.330916Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-03-27T14:05:33.331450Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T14:05:33.330916Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T14:05:33.331450Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - TypeError: unsupported operand type(s) for ^: 'BoxList' and 'BoxList'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.16s
operator: core/ReplaceBinaryOperator_Sub_Add, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -720,7 +720,7 @@
                 comment,
             )
 
-            duration = time.time() - start_time
+            duration = time.time() + start_time
             pr_approval_duration_seconds.observe(duration)
 
             status = "success" if success else "failure"
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Add, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -784,7 +784,7 @@
                         pr_number,
                         extra={"repo": repo_full_name, "pr_number": pr_number},
                     )
-                    duration = time.time() - start_time
+                    duration = time.time() + start_time
                     pr_dismissal_duration_seconds.observe(duration)
                     pr_dismissals_total.labels(trigger_type=trigger_type, status="success").inc()
                     add_span_attributes(span, {"dismissal.result": "no_reviews"})
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_Add, occurrence: 2
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -804,7 +804,7 @@
                     )
                     success = success and result
 
-                duration = time.time() - start_time
+                duration = time.time() + start_time
                 pr_dismissal_duration_seconds.observe(duration)
 
                 status = "success" if success else "failure"
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_Add, occurrence: 3
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -816,7 +816,7 @@
                 return success
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() + start_time
                 pr_dismissal_duration_seconds.observe(duration)
                 pr_dismissals_total.labels(trigger_type=trigger_type, status="failure").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_Mul, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -720,7 +720,7 @@
                 comment,
             )
 
-            duration = time.time() - start_time
+            duration = time.time() * start_time
             pr_approval_duration_seconds.observe(duration)
 
             status = "success" if success else "failure"
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_Mul, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -784,7 +784,7 @@
                         pr_number,
                         extra={"repo": repo_full_name, "pr_number": pr_number},
                     )
-                    duration = time.time() - start_time
+                    duration = time.time() * start_time
                     pr_dismissal_duration_seconds.observe(duration)
                     pr_dismissals_total.labels(trigger_type=trigger_type, status="success").inc()
                     add_span_attributes(span, {"dismissal.result": "no_reviews"})
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_Mul, occurrence: 2
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -804,7 +804,7 @@
                     )
                     success = success and result
 
-                duration = time.time() - start_time
+                duration = time.time() * start_time
                 pr_dismissal_duration_seconds.observe(duration)
 
                 status = "success" if success else "failure"
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Mul, occurrence: 3
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -816,7 +816,7 @@
                 return success
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() * start_time
                 pr_dismissal_duration_seconds.observe(duration)
                 pr_dismissals_total.labels(trigger_type=trigger_type, status="failure").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_Div, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -720,7 +720,7 @@
                 comment,
             )
 
-            duration = time.time() - start_time
+            duration = time.time() / start_time
             pr_approval_duration_seconds.observe(duration)
 
             status = "success" if success else "failure"
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Div, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -784,7 +784,7 @@
                         pr_number,
                         extra={"repo": repo_full_name, "pr_number": pr_number},
                     )
-                    duration = time.time() - start_time
+                    duration = time.time() / start_time
                     pr_dismissal_duration_seconds.observe(duration)
                     pr_dismissals_total.labels(trigger_type=trigger_type, status="success").inc()
                     add_span_attributes(span, {"dismissal.result": "no_reviews"})
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Div, occurrence: 2
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -804,7 +804,7 @@
                     )
                     success = success and result
 
-                duration = time.time() - start_time
+                duration = time.time() / start_time
                 pr_dismissal_duration_seconds.observe(duration)
 
                 status = "success" if success else "failure"
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceBinaryOperator_Sub_Div, occurrence: 3
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -816,7 +816,7 @@
                 return success
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() / start_time
                 pr_dismissal_duration_seconds.observe(duration)
                 pr_dismissals_total.labels(trigger_type=trigger_type, status="failure").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/ReplaceBinaryOperator_Sub_FloorDiv, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -720,7 +720,7 @@
                 comment,
             )
 
-            duration = time.time() - start_time
+            duration = time.time() // start_time
             pr_approval_duration_seconds.observe(duration)
 
             status = "success" if success else "failure"
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_FloorDiv, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -784,7 +784,7 @@
                         pr_number,
                         extra={"repo": repo_full_name, "pr_number": pr_number},
                     )
-                    duration = time.time() - start_time
+                    duration = time.time() // start_time
                     pr_dismissal_duration_seconds.observe(duration)
                     pr_dismissals_total.labels(trigger_type=trigger_type, status="success").inc()
                     add_span_attributes(span, {"dismissal.result": "no_reviews"})
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/ReplaceBinaryOperator_Sub_FloorDiv, occurrence: 2
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -804,7 +804,7 @@
                     )
                     success = success and result
 
-                duration = time.time() - start_time
+                duration = time.time() // start_time
                 pr_dismissal_duration_seconds.observe(duration)
 
                 status = "success" if success else "failure"
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_FloorDiv, occurrence: 3
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -816,7 +816,7 @@
                 return success
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() // start_time
                 pr_dismissal_duration_seconds.observe(duration)
                 pr_dismissals_total.labels(trigger_type=trigger_type, status="failure").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_Sub_Mod, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -720,7 +720,7 @@
                 comment,
             )
 
-            duration = time.time() - start_time
+            duration = time.time() % start_time
             pr_approval_duration_seconds.observe(duration)
 
             status = "success" if success else "failure"
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_Mod, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -784,7 +784,7 @@
                         pr_number,
                         extra={"repo": repo_full_name, "pr_number": pr_number},
                     )
-                    duration = time.time() - start_time
+                    duration = time.time() % start_time
                     pr_dismissal_duration_seconds.observe(duration)
                     pr_dismissals_total.labels(trigger_type=trigger_type, status="success").inc()
                     add_span_attributes(span, {"dismissal.result": "no_reviews"})
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_Mod, occurrence: 2
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -804,7 +804,7 @@
                     )
                     success = success and result
 
-                duration = time.time() - start_time
+                duration = time.time() % start_time
                 pr_dismissal_duration_seconds.observe(duration)
 
                 status = "success" if success else "failure"
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/ReplaceBinaryOperator_Sub_Mod, occurrence: 3
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -816,7 +816,7 @@
                 return success
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() % start_time
                 pr_dismissal_duration_seconds.observe(duration)
                 pr_dismissals_total.labels(trigger_type=trigger_type, status="failure").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_Sub_Pow, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -720,7 +720,7 @@
                 comment,
             )
 
-            duration = time.time() - start_time
+            duration = time.time() ** start_time
             pr_approval_duration_seconds.observe(duration)
 
             status = "success" if success else "failure"
........................................................................ [ 34%]
........................................................................ [ 68%]
...................F
=================================== FAILURES ===================================
____________________ test_pr_opened_with_autoapprove_label _____________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fa140ae8050>
mock_github_client = <MagicMock name='github_client' id='140330550117440'>

    @pytest.mark.asyncio
    async def test_pr_opened_with_autoapprove_label(webhook_handler, mock_github_client):
        """Test PR opened with autoapprove label triggers approval."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
    
>       result = await webhook_handler.handle_event("pull_request", payload)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_webhook_handler.py:68: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/webhook_handler.py:113: in handle_event
    result = await self._handle_pull_request(payload)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/webhook_handler.py:290: in _handle_pull_request
    success = await self._approve_pr(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7fa140ae8050>
installation_id = 12345, repo_full_name = 'octocat/hello-world', pr_number = 42
comment = 'Auto-approved by Stampbot (label: autoapprove)'
trigger_type = 'label'

    async def _approve_pr(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        comment: str,
        trigger_type: str,
    ) -> bool:
        """Approve a PR and track metrics.
    
        Checks for existing active approvals first to avoid duplicate comments.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name
            pr_number: PR number
            comment: Approval comment
            trigger_type: What triggered the approval (label, chatops)
    
        Returns:
            True if successful (or already approved)
        """
        with create_span(
            "webhook.approve_pr",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "approval.trigger_type": trigger_type,
            },
        ) as span:
            # Check for existing active approval to avoid duplicates
            existing_approvals = await run_in_threadpool(
                github_client.find_bot_reviews,
                installation_id,
                repo_full_name,
                pr_number,
            )
    
            if existing_approvals:
                logger.info(
                    "PR #%d in %s already has active approval, skipping",
                    pr_number,
                    repo_full_name,
                    extra={
                        "repo": repo_full_name,
                        "pr_number": pr_number,
                        "existing_review_ids": existing_approvals,
                    },
                )
                add_span_attributes(
                    span,
                    {
                        "approval.result": "already_approved",
                        "approval.existing_reviews": len(existing_approvals),
                    },
                )
                set_span_ok(span)
                return True
    
            start_time = time.time()
    
            success = await run_in_threadpool(
                github_client.approve_pr,
                installation_id,
                repo_full_name,
                pr_number,
                comment,
            )
    
>           duration = time.time() ** start_time
                       ^^^^^^^^^^^^^^^^^^^^^^^^^
E           OverflowError: (34, 'Numerical result out of range')

stampbot/webhook_handler.py:723: OverflowError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:46:17.314671Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T13:46:17.315352Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T13:46:17.316203Z [info     ] PR #42 has approval label: autoapprove _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:46:17.314671Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:46:17.315352Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:278 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'PR #42 has approval label: autoapprove', 'level': 'info', 'timestamp': '2026-03-27T13:46:17.316203Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_opened_with_autoapprove_label - OverflowError: (34, 'Numerical result out of range')
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 163 passed, 3 deselected in 1.12s
operator: core/ReplaceBinaryOperator_Sub_Pow, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -784,7 +784,7 @@
                         pr_number,
                         extra={"repo": repo_full_name, "pr_number": pr_number},
                     )
-                    duration = time.time() - start_time
+                    duration = time.time() ** start_time
                     pr_dismissal_duration_seconds.observe(duration)
                     pr_dismissals_total.labels(trigger_type=trigger_type, status="success").inc()
                     add_span_attributes(span, {"dismissal.result": "no_reviews"})
........................................................................ [ 34%]
........................................................................ [ 68%]
...........................F
=================================== FAILURES ===================================
_______________________ test_pr_unlabeled_no_bot_reviews _______________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f3393aa95e0>
mock_github_client = <MagicMock name='github_client' id='139859493087824'>

    @pytest.mark.asyncio
    async def test_pr_unlabeled_no_bot_reviews(webhook_handler, mock_github_client):
        """Test removing label when no bot reviews exist."""
        payload = load_fixture("pr_unlabeled_autoapprove")
        mock_github_client.find_bot_reviews.return_value = []  # No reviews to dismiss
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'error' == 'success'
E         
E         - success
E         + error

tests/test_webhook_handler.py:179: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:04:13.681055Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-03-27T14:04:13.681578Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T14:04:13.682107Z [info     ] Approval label autoapprove removed from PR #42 _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
2026-03-27T14:04:13.682506Z [info     ] No bot approvals found on PR #42 _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42}
2026-03-27T14:04:13.682709Z [error    ] Error dismissing approvals: (34, 'Numerical result out of range') _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=error extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'error': "(34, 'Numerical result out of range')"}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'unlabeled'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T14:04:13.681055Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T14:04:13.681578Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:320 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'Approval label autoapprove removed from PR #42', 'level': 'info', 'timestamp': '2026-03-27T14:04:13.682107Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:782 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42}, 'event': 'No bot approvals found on PR #42', 'level': 'info', 'timestamp': '2026-03-27T14:04:13.682506Z'}
ERROR    stampbot.webhook_handler:webhook_handler.py:823 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'error': "(34, 'Numerical result out of range')"}, 'event': "Error dismissing approvals: (34, 'Numerical result out of range')", 'level': 'error', 'timestamp': '2026-03-27T14:04:13.682709Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_unlabeled_no_bot_reviews - AssertionError: assert 'error' == 'success'
  
  - success
  + error
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 171 passed, 3 deselected in 1.11s
operator: core/ReplaceBinaryOperator_Sub_Pow, occurrence: 2
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -804,7 +804,7 @@
                     )
                     success = success and result
 
-                duration = time.time() - start_time
+                duration = time.time() ** start_time
                 pr_dismissal_duration_seconds.observe(duration)
 
                 status = "success" if success else "failure"
........................................................................ [ 34%]
........................................................................ [ 68%]
.........................F
=================================== FAILURES ===================================
________________________ test_pr_unlabeled_autoapprove _________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f8f19b11550>
mock_github_client = <MagicMock name='github_client' id='140252655852848'>

    @pytest.mark.asyncio
    async def test_pr_unlabeled_autoapprove(webhook_handler, mock_github_client):
        """Test removing autoapprove label dismisses approvals."""
        payload = load_fixture("pr_unlabeled_autoapprove")
        mock_github_client.find_bot_reviews.return_value = [111, 222]  # Mock review IDs
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'error' == 'success'
E         
E         - success
E         + error

tests/test_webhook_handler.py:152: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:56:37.481850Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-03-27T13:56:37.482384Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T13:56:37.482959Z [info     ] Approval label autoapprove removed from PR #42 _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
2026-03-27T13:56:37.483662Z [error    ] Error dismissing approvals: (34, 'Numerical result out of range') _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=error extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'error': "(34, 'Numerical result out of range')"}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'unlabeled'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:56:37.481850Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:56:37.482384Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:320 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'Approval label autoapprove removed from PR #42', 'level': 'info', 'timestamp': '2026-03-27T13:56:37.482959Z'}
ERROR    stampbot.webhook_handler:webhook_handler.py:823 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'error': "(34, 'Numerical result out of range')"}, 'event': "Error dismissing approvals: (34, 'Numerical result out of range')", 'level': 'error', 'timestamp': '2026-03-27T13:56:37.483662Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_unlabeled_autoapprove - AssertionError: assert 'error' == 'success'
  
  - success
  + error
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 169 passed, 3 deselected in 1.10s
operator: core/ReplaceBinaryOperator_Sub_Pow, occurrence: 3
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -816,7 +816,7 @@
                 return success
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() ** start_time
                 pr_dismissal_duration_seconds.observe(duration)
                 pr_dismissals_total.labels(trigger_type=trigger_type, status="failure").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
............................................................F
=================================== FAILURES ===================================
_______________________ test_dismiss_approvals_exception _______________________

self = <stampbot.webhook_handler.WebhookHandler object at 0x7fe3bcd7bef0>
installation_id = 12345, repo_full_name = 'octocat/hello-world', pr_number = 42
message = 'Label autoapprove removed', trigger_type = 'label_removed'

    async def _dismiss_approvals(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        message: str,
        trigger_type: str,
    ) -> bool:
        """Dismiss bot approvals on a PR.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name
            pr_number: PR number
            message: Dismissal message
            trigger_type: What triggered the dismissal (label_removed, chatops)
    
        Returns:
            True if successful
        """
        with create_span(
            "webhook.dismiss_approvals",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "dismissal.trigger_type": trigger_type,
            },
        ) as span:
            start_time = time.time()
    
            try:
                # Find all bot reviews
>               review_ids = await run_in_threadpool(
                    github_client.find_bot_reviews,
                    installation_id,
                    repo_full_name,
                    pr_number,
                )

stampbot/webhook_handler.py:772: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/concurrency.py:32: in run_in_threadpool
    return await anyio.to_thread.run_sync(func)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/to_thread.py:63: in run_sync
    return await get_async_backend().run_sync_in_worker_thread(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/_backends/_asyncio.py:2502: in run_sync_in_worker_thread
    return await future
           ^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/_backends/_asyncio.py:986: in run
    result = context.run(func, *args)
             ^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <MagicMock name='github_client.find_bot_reviews' id='140616094815904'>
args = (12345, 'octocat/hello-world', 42), kwargs = {}
effect = Exception('API error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fe3bcd7bef0>
mock_github_client = <MagicMock name='github_client' id='140616094814224'>

    @pytest.mark.asyncio
    async def test_dismiss_approvals_exception(webhook_handler, mock_github_client):
        """Test _dismiss_approvals handles exceptions gracefully."""
        payload = load_fixture("pr_unlabeled_autoapprove")
    
        # Make find_bot_reviews raise an exception
        mock_github_client.find_bot_reviews.side_effect = Exception("API error")
    
>       result = await webhook_handler.handle_event("pull_request", payload)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_webhook_handler.py:727: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/webhook_handler.py:113: in handle_event
    result = await self._handle_pull_request(payload)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/webhook_handler.py:332: in _handle_pull_request
    success = await self._dismiss_approvals(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7fe3bcd7bef0>
installation_id = 12345, repo_full_name = 'octocat/hello-world', pr_number = 42
message = 'Label autoapprove removed', trigger_type = 'label_removed'

    async def _dismiss_approvals(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        message: str,
        trigger_type: str,
    ) -> bool:
        """Dismiss bot approvals on a PR.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name
            pr_number: PR number
            message: Dismissal message
            trigger_type: What triggered the dismissal (label_removed, chatops)
    
        Returns:
            True if successful
        """
        with create_span(
            "webhook.dismiss_approvals",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "dismissal.trigger_type": trigger_type,
            },
        ) as span:
            start_time = time.time()
    
            try:
                # Find all bot reviews
                review_ids = await run_in_threadpool(
                    github_client.find_bot_reviews,
                    installation_id,
                    repo_full_name,
                    pr_number,
                )
    
                add_span_attributes(span, {"dismissal.reviews_found": len(review_ids)})
    
                if not review_ids:
                    logger.info(
                        "No bot approvals found on PR #%d",
                        pr_number,
                        extra={"repo": repo_full_name, "pr_number": pr_number},
                    )
                    duration = time.time() - start_time
                    pr_dismissal_duration_seconds.observe(duration)
                    pr_dismissals_total.labels(trigger_type=trigger_type, status="success").inc()
                    add_span_attributes(span, {"dismissal.result": "no_reviews"})
                    set_span_ok(span)
                    return True
    
                # Dismiss each review
                success = True
                for review_id in review_ids:
                    result = await run_in_threadpool(
                        github_client.dismiss_approval,
                        installation_id,
                        repo_full_name,
                        pr_number,
                        review_id,
                        message,
                    )
                    success = success and result
    
                duration = time.time() - start_time
                pr_dismissal_duration_seconds.observe(duration)
    
                status = "success" if success else "failure"
                pr_dismissals_total.labels(trigger_type=trigger_type, status=status).inc()
    
                add_span_attributes(span, {"dismissal.result": status})
                set_span_ok(span)
    
                return success
    
            except Exception as e:
>               duration = time.time() ** start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^^
E               OverflowError: (34, 'Numerical result out of range')

stampbot/webhook_handler.py:819: OverflowError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:02:37.352283Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-03-27T14:02:37.352849Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T14:02:37.353381Z [info     ] Approval label autoapprove removed from PR #42 _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'unlabeled'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T14:02:37.352283Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T14:02:37.352849Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:320 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'Approval label autoapprove removed from PR #42', 'level': 'info', 'timestamp': '2026-03-27T14:02:37.353381Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_dismiss_approvals_exception - OverflowError: (34, 'Numerical result out of range')
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 204 passed, 3 deselected in 1.82s
operator: core/ReplaceBinaryOperator_Sub_RShift, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -720,7 +720,7 @@
                 comment,
             )
 
-            duration = time.time() - start_time
+            duration = time.time() >> start_time
             pr_approval_duration_seconds.observe(duration)
 
             status = "success" if success else "failure"
........................................................................ [ 34%]
........................................................................ [ 68%]
...................F
=================================== FAILURES ===================================
____________________ test_pr_opened_with_autoapprove_label _____________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fc07ab20050>
mock_github_client = <MagicMock name='github_client' id='140464669508672'>

    @pytest.mark.asyncio
    async def test_pr_opened_with_autoapprove_label(webhook_handler, mock_github_client):
        """Test PR opened with autoapprove label triggers approval."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
    
>       result = await webhook_handler.handle_event("pull_request", payload)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_webhook_handler.py:68: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/webhook_handler.py:113: in handle_event
    result = await self._handle_pull_request(payload)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/webhook_handler.py:290: in _handle_pull_request
    success = await self._approve_pr(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7fc07ab20050>
installation_id = 12345, repo_full_name = 'octocat/hello-world', pr_number = 42
comment = 'Auto-approved by Stampbot (label: autoapprove)'
trigger_type = 'label'

    async def _approve_pr(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        comment: str,
        trigger_type: str,
    ) -> bool:
        """Approve a PR and track metrics.
    
        Checks for existing active approvals first to avoid duplicate comments.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name
            pr_number: PR number
            comment: Approval comment
            trigger_type: What triggered the approval (label, chatops)
    
        Returns:
            True if successful (or already approved)
        """
        with create_span(
            "webhook.approve_pr",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "approval.trigger_type": trigger_type,
            },
        ) as span:
            # Check for existing active approval to avoid duplicates
            existing_approvals = await run_in_threadpool(
                github_client.find_bot_reviews,
                installation_id,
                repo_full_name,
                pr_number,
            )
    
            if existing_approvals:
                logger.info(
                    "PR #%d in %s already has active approval, skipping",
                    pr_number,
                    repo_full_name,
                    extra={
                        "repo": repo_full_name,
                        "pr_number": pr_number,
                        "existing_review_ids": existing_approvals,
                    },
                )
                add_span_attributes(
                    span,
                    {
                        "approval.result": "already_approved",
                        "approval.existing_reviews": len(existing_approvals),
                    },
                )
                set_span_ok(span)
                return True
    
            start_time = time.time()
    
            success = await run_in_threadpool(
                github_client.approve_pr,
                installation_id,
                repo_full_name,
                pr_number,
                comment,
            )
    
>           duration = time.time() >> start_time
                       ^^^^^^^^^^^^^^^^^^^^^^^^^
E           TypeError: unsupported operand type(s) for >>: 'float' and 'float'

stampbot/webhook_handler.py:723: TypeError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:55:04.101641Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T13:55:04.102322Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T13:55:04.103205Z [info     ] PR #42 has approval label: autoapprove _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:55:04.101641Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:55:04.102322Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:278 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'PR #42 has approval label: autoapprove', 'level': 'info', 'timestamp': '2026-03-27T13:55:04.103205Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_opened_with_autoapprove_label - TypeError: unsupported operand type(s) for >>: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 163 passed, 3 deselected in 1.13s
operator: core/ReplaceBinaryOperator_Sub_RShift, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -784,7 +784,7 @@
                         pr_number,
                         extra={"repo": repo_full_name, "pr_number": pr_number},
                     )
-                    duration = time.time() - start_time
+                    duration = time.time() >> start_time
                     pr_dismissal_duration_seconds.observe(duration)
                     pr_dismissals_total.labels(trigger_type=trigger_type, status="success").inc()
                     add_span_attributes(span, {"dismissal.result": "no_reviews"})
........................................................................ [ 34%]
........................................................................ [ 68%]
...........................F
=================================== FAILURES ===================================
_______________________ test_pr_unlabeled_no_bot_reviews _______________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f888c9b95e0>
mock_github_client = <MagicMock name='github_client' id='140224446884432'>

    @pytest.mark.asyncio
    async def test_pr_unlabeled_no_bot_reviews(webhook_handler, mock_github_client):
        """Test removing label when no bot reviews exist."""
        payload = load_fixture("pr_unlabeled_autoapprove")
        mock_github_client.find_bot_reviews.return_value = []  # No reviews to dismiss
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'error' == 'success'
E         
E         - success
E         + error

tests/test_webhook_handler.py:179: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:43:50.932369Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-03-27T13:43:50.932910Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T13:43:50.933417Z [info     ] Approval label autoapprove removed from PR #42 _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
2026-03-27T13:43:50.933838Z [info     ] No bot approvals found on PR #42 _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42}
2026-03-27T13:43:50.934045Z [error    ] Error dismissing approvals: unsupported operand type(s) for >>: 'float' and 'float' _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=error extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'error': "unsupported operand type(s) for >>: 'float' and 'float'"}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'unlabeled'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:43:50.932369Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:43:50.932910Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:320 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'Approval label autoapprove removed from PR #42', 'level': 'info', 'timestamp': '2026-03-27T13:43:50.933417Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:782 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42}, 'event': 'No bot approvals found on PR #42', 'level': 'info', 'timestamp': '2026-03-27T13:43:50.933838Z'}
ERROR    stampbot.webhook_handler:webhook_handler.py:823 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'error': "unsupported operand type(s) for >>: 'float' and 'float'"}, 'event': "Error dismissing approvals: unsupported operand type(s) for >>: 'float' and 'float'", 'level': 'error', 'timestamp': '2026-03-27T13:43:50.934045Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_unlabeled_no_bot_reviews - AssertionError: assert 'error' == 'success'
  
  - success
  + error
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 171 passed, 3 deselected in 1.10s
operator: core/ReplaceBinaryOperator_Sub_RShift, occurrence: 2
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -804,7 +804,7 @@
                     )
                     success = success and result
 
-                duration = time.time() - start_time
+                duration = time.time() >> start_time
                 pr_dismissal_duration_seconds.observe(duration)
 
                 status = "success" if success else "failure"
........................................................................ [ 34%]
........................................................................ [ 68%]
.........................F
=================================== FAILURES ===================================
________________________ test_pr_unlabeled_autoapprove _________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f577ad01650>
mock_github_client = <MagicMock name='github_client' id='140013766860080'>

    @pytest.mark.asyncio
    async def test_pr_unlabeled_autoapprove(webhook_handler, mock_github_client):
        """Test removing autoapprove label dismisses approvals."""
        payload = load_fixture("pr_unlabeled_autoapprove")
        mock_github_client.find_bot_reviews.return_value = [111, 222]  # Mock review IDs
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'error' == 'success'
E         
E         - success
E         + error

tests/test_webhook_handler.py:152: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:07:12.715418Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-03-27T14:07:12.715952Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T14:07:12.716476Z [info     ] Approval label autoapprove removed from PR #42 _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
2026-03-27T14:07:12.717155Z [error    ] Error dismissing approvals: unsupported operand type(s) for >>: 'float' and 'float' _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=error extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'error': "unsupported operand type(s) for >>: 'float' and 'float'"}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'unlabeled'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T14:07:12.715418Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T14:07:12.715952Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:320 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'Approval label autoapprove removed from PR #42', 'level': 'info', 'timestamp': '2026-03-27T14:07:12.716476Z'}
ERROR    stampbot.webhook_handler:webhook_handler.py:823 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'error': "unsupported operand type(s) for >>: 'float' and 'float'"}, 'event': "Error dismissing approvals: unsupported operand type(s) for >>: 'float' and 'float'", 'level': 'error', 'timestamp': '2026-03-27T14:07:12.717155Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_unlabeled_autoapprove - AssertionError: assert 'error' == 'success'
  
  - success
  + error
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 169 passed, 3 deselected in 1.11s
operator: core/ReplaceBinaryOperator_Sub_RShift, occurrence: 3
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -816,7 +816,7 @@
                 return success
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() >> start_time
                 pr_dismissal_duration_seconds.observe(duration)
                 pr_dismissals_total.labels(trigger_type=trigger_type, status="failure").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
............................................................F
=================================== FAILURES ===================================
_______________________ test_dismiss_approvals_exception _______________________

self = <stampbot.webhook_handler.WebhookHandler object at 0x7f536a08bef0>
installation_id = 12345, repo_full_name = 'octocat/hello-world', pr_number = 42
message = 'Label autoapprove removed', trigger_type = 'label_removed'

    async def _dismiss_approvals(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        message: str,
        trigger_type: str,
    ) -> bool:
        """Dismiss bot approvals on a PR.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name
            pr_number: PR number
            message: Dismissal message
            trigger_type: What triggered the dismissal (label_removed, chatops)
    
        Returns:
            True if successful
        """
        with create_span(
            "webhook.dismiss_approvals",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "dismissal.trigger_type": trigger_type,
            },
        ) as span:
            start_time = time.time()
    
            try:
                # Find all bot reviews
>               review_ids = await run_in_threadpool(
                    github_client.find_bot_reviews,
                    installation_id,
                    repo_full_name,
                    pr_number,
                )

stampbot/webhook_handler.py:772: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/concurrency.py:32: in run_in_threadpool
    return await anyio.to_thread.run_sync(func)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/to_thread.py:63: in run_sync
    return await get_async_backend().run_sync_in_worker_thread(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/_backends/_asyncio.py:2502: in run_sync_in_worker_thread
    return await future
           ^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/_backends/_asyncio.py:986: in run
    result = context.run(func, *args)
             ^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <MagicMock name='github_client.find_bot_reviews' id='139996232226464'>
args = (12345, 'octocat/hello-world', 42), kwargs = {}
effect = Exception('API error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f536a08bef0>
mock_github_client = <MagicMock name='github_client' id='139996232224784'>

    @pytest.mark.asyncio
    async def test_dismiss_approvals_exception(webhook_handler, mock_github_client):
        """Test _dismiss_approvals handles exceptions gracefully."""
        payload = load_fixture("pr_unlabeled_autoapprove")
    
        # Make find_bot_reviews raise an exception
        mock_github_client.find_bot_reviews.side_effect = Exception("API error")
    
>       result = await webhook_handler.handle_event("pull_request", payload)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_webhook_handler.py:727: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/webhook_handler.py:113: in handle_event
    result = await self._handle_pull_request(payload)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/webhook_handler.py:332: in _handle_pull_request
    success = await self._dismiss_approvals(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7f536a08bef0>
installation_id = 12345, repo_full_name = 'octocat/hello-world', pr_number = 42
message = 'Label autoapprove removed', trigger_type = 'label_removed'

    async def _dismiss_approvals(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        message: str,
        trigger_type: str,
    ) -> bool:
        """Dismiss bot approvals on a PR.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name
            pr_number: PR number
            message: Dismissal message
            trigger_type: What triggered the dismissal (label_removed, chatops)
    
        Returns:
            True if successful
        """
        with create_span(
            "webhook.dismiss_approvals",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "dismissal.trigger_type": trigger_type,
            },
        ) as span:
            start_time = time.time()
    
            try:
                # Find all bot reviews
                review_ids = await run_in_threadpool(
                    github_client.find_bot_reviews,
                    installation_id,
                    repo_full_name,
                    pr_number,
                )
    
                add_span_attributes(span, {"dismissal.reviews_found": len(review_ids)})
    
                if not review_ids:
                    logger.info(
                        "No bot approvals found on PR #%d",
                        pr_number,
                        extra={"repo": repo_full_name, "pr_number": pr_number},
                    )
                    duration = time.time() - start_time
                    pr_dismissal_duration_seconds.observe(duration)
                    pr_dismissals_total.labels(trigger_type=trigger_type, status="success").inc()
                    add_span_attributes(span, {"dismissal.result": "no_reviews"})
                    set_span_ok(span)
                    return True
    
                # Dismiss each review
                success = True
                for review_id in review_ids:
                    result = await run_in_threadpool(
                        github_client.dismiss_approval,
                        installation_id,
                        repo_full_name,
                        pr_number,
                        review_id,
                        message,
                    )
                    success = success and result
    
                duration = time.time() - start_time
                pr_dismissal_duration_seconds.observe(duration)
    
                status = "success" if success else "failure"
                pr_dismissals_total.labels(trigger_type=trigger_type, status=status).inc()
    
                add_span_attributes(span, {"dismissal.result": status})
                set_span_ok(span)
    
                return success
    
            except Exception as e:
>               duration = time.time() >> start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for >>: 'float' and 'float'

stampbot/webhook_handler.py:819: TypeError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:30:14.138349Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-03-27T13:30:14.138917Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T13:30:14.139456Z [info     ] Approval label autoapprove removed from PR #42 _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'unlabeled'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:30:14.138349Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:30:14.138917Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:320 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'Approval label autoapprove removed from PR #42', 'level': 'info', 'timestamp': '2026-03-27T13:30:14.139456Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_dismiss_approvals_exception - TypeError: unsupported operand type(s) for >>: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 204 passed, 3 deselected in 1.81s
operator: core/ReplaceBinaryOperator_Sub_LShift, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -720,7 +720,7 @@
                 comment,
             )
 
-            duration = time.time() - start_time
+            duration = time.time() << start_time
             pr_approval_duration_seconds.observe(duration)
 
             status = "success" if success else "failure"
........................................................................ [ 34%]
........................................................................ [ 68%]
...................F
=================================== FAILURES ===================================
____________________ test_pr_opened_with_autoapprove_label _____________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f15f5b14050>
mock_github_client = <MagicMock name='github_client' id='139732293649472'>

    @pytest.mark.asyncio
    async def test_pr_opened_with_autoapprove_label(webhook_handler, mock_github_client):
        """Test PR opened with autoapprove label triggers approval."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
    
>       result = await webhook_handler.handle_event("pull_request", payload)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_webhook_handler.py:68: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/webhook_handler.py:113: in handle_event
    result = await self._handle_pull_request(payload)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/webhook_handler.py:290: in _handle_pull_request
    success = await self._approve_pr(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7f15f5b14050>
installation_id = 12345, repo_full_name = 'octocat/hello-world', pr_number = 42
comment = 'Auto-approved by Stampbot (label: autoapprove)'
trigger_type = 'label'

    async def _approve_pr(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        comment: str,
        trigger_type: str,
    ) -> bool:
        """Approve a PR and track metrics.
    
        Checks for existing active approvals first to avoid duplicate comments.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name
            pr_number: PR number
            comment: Approval comment
            trigger_type: What triggered the approval (label, chatops)
    
        Returns:
            True if successful (or already approved)
        """
        with create_span(
            "webhook.approve_pr",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "approval.trigger_type": trigger_type,
            },
        ) as span:
            # Check for existing active approval to avoid duplicates
            existing_approvals = await run_in_threadpool(
                github_client.find_bot_reviews,
                installation_id,
                repo_full_name,
                pr_number,
            )
    
            if existing_approvals:
                logger.info(
                    "PR #%d in %s already has active approval, skipping",
                    pr_number,
                    repo_full_name,
                    extra={
                        "repo": repo_full_name,
                        "pr_number": pr_number,
                        "existing_review_ids": existing_approvals,
                    },
                )
                add_span_attributes(
                    span,
                    {
                        "approval.result": "already_approved",
                        "approval.existing_reviews": len(existing_approvals),
                    },
                )
                set_span_ok(span)
                return True
    
            start_time = time.time()
    
            success = await run_in_threadpool(
                github_client.approve_pr,
                installation_id,
                repo_full_name,
                pr_number,
                comment,
            )
    
>           duration = time.time() << start_time
                       ^^^^^^^^^^^^^^^^^^^^^^^^^
E           TypeError: unsupported operand type(s) for <<: 'float' and 'float'

stampbot/webhook_handler.py:723: TypeError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:30:06.528652Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T13:30:06.529314Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T13:30:06.530198Z [info     ] PR #42 has approval label: autoapprove _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:30:06.528652Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:30:06.529314Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:278 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'PR #42 has approval label: autoapprove', 'level': 'info', 'timestamp': '2026-03-27T13:30:06.530198Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_opened_with_autoapprove_label - TypeError: unsupported operand type(s) for <<: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 163 passed, 3 deselected in 1.11s
operator: core/ReplaceBinaryOperator_Sub_LShift, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -784,7 +784,7 @@
                         pr_number,
                         extra={"repo": repo_full_name, "pr_number": pr_number},
                     )
-                    duration = time.time() - start_time
+                    duration = time.time() << start_time
                     pr_dismissal_duration_seconds.observe(duration)
                     pr_dismissals_total.labels(trigger_type=trigger_type, status="success").inc()
                     add_span_attributes(span, {"dismissal.result": "no_reviews"})
........................................................................ [ 34%]
........................................................................ [ 68%]
...........................F
=================================== FAILURES ===================================
_______________________ test_pr_unlabeled_no_bot_reviews _______________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fbe3aecd5e0>
mock_github_client = <MagicMock name='github_client' id='140455004711504'>

    @pytest.mark.asyncio
    async def test_pr_unlabeled_no_bot_reviews(webhook_handler, mock_github_client):
        """Test removing label when no bot reviews exist."""
        payload = load_fixture("pr_unlabeled_autoapprove")
        mock_github_client.find_bot_reviews.return_value = []  # No reviews to dismiss
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'error' == 'success'
E         
E         - success
E         + error

tests/test_webhook_handler.py:179: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:07:55.088373Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-03-27T14:07:55.088959Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T14:07:55.089476Z [info     ] Approval label autoapprove removed from PR #42 _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
2026-03-27T14:07:55.089966Z [info     ] No bot approvals found on PR #42 _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42}
2026-03-27T14:07:55.090188Z [error    ] Error dismissing approvals: unsupported operand type(s) for <<: 'float' and 'float' _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=error extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'error': "unsupported operand type(s) for <<: 'float' and 'float'"}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'unlabeled'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T14:07:55.088373Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T14:07:55.088959Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:320 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'Approval label autoapprove removed from PR #42', 'level': 'info', 'timestamp': '2026-03-27T14:07:55.089476Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:782 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42}, 'event': 'No bot approvals found on PR #42', 'level': 'info', 'timestamp': '2026-03-27T14:07:55.089966Z'}
ERROR    stampbot.webhook_handler:webhook_handler.py:823 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'error': "unsupported operand type(s) for <<: 'float' and 'float'"}, 'event': "Error dismissing approvals: unsupported operand type(s) for <<: 'float' and 'float'", 'level': 'error', 'timestamp': '2026-03-27T14:07:55.090188Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_unlabeled_no_bot_reviews - AssertionError: assert 'error' == 'success'
  
  - success
  + error
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 171 passed, 3 deselected in 1.14s
operator: core/ReplaceBinaryOperator_Sub_LShift, occurrence: 2
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -804,7 +804,7 @@
                     )
                     success = success and result
 
-                duration = time.time() - start_time
+                duration = time.time() << start_time
                 pr_dismissal_duration_seconds.observe(duration)
 
                 status = "success" if success else "failure"
........................................................................ [ 34%]
........................................................................ [ 68%]
.........................F
=================================== FAILURES ===================================
________________________ test_pr_unlabeled_autoapprove _________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f3cfdff9550>
mock_github_client = <MagicMock name='github_client' id='139900008045872'>

    @pytest.mark.asyncio
    async def test_pr_unlabeled_autoapprove(webhook_handler, mock_github_client):
        """Test removing autoapprove label dismisses approvals."""
        payload = load_fixture("pr_unlabeled_autoapprove")
        mock_github_client.find_bot_reviews.return_value = [111, 222]  # Mock review IDs
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'error' == 'success'
E         
E         - success
E         + error

tests/test_webhook_handler.py:152: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:02:15.171860Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-03-27T14:02:15.172388Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T14:02:15.172928Z [info     ] Approval label autoapprove removed from PR #42 _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
2026-03-27T14:02:15.173596Z [error    ] Error dismissing approvals: unsupported operand type(s) for <<: 'float' and 'float' _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=error extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'error': "unsupported operand type(s) for <<: 'float' and 'float'"}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'unlabeled'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T14:02:15.171860Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T14:02:15.172388Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:320 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'Approval label autoapprove removed from PR #42', 'level': 'info', 'timestamp': '2026-03-27T14:02:15.172928Z'}
ERROR    stampbot.webhook_handler:webhook_handler.py:823 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'error': "unsupported operand type(s) for <<: 'float' and 'float'"}, 'event': "Error dismissing approvals: unsupported operand type(s) for <<: 'float' and 'float'", 'level': 'error', 'timestamp': '2026-03-27T14:02:15.173596Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_unlabeled_autoapprove - AssertionError: assert 'error' == 'success'
  
  - success
  + error
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 169 passed, 3 deselected in 1.10s
operator: core/ReplaceBinaryOperator_Sub_LShift, occurrence: 3
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -816,7 +816,7 @@
                 return success
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() << start_time
                 pr_dismissal_duration_seconds.observe(duration)
                 pr_dismissals_total.labels(trigger_type=trigger_type, status="failure").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
............................................................F
=================================== FAILURES ===================================
_______________________ test_dismiss_approvals_exception _______________________

self = <stampbot.webhook_handler.WebhookHandler object at 0x7fb20bf6fef0>
installation_id = 12345, repo_full_name = 'octocat/hello-world', pr_number = 42
message = 'Label autoapprove removed', trigger_type = 'label_removed'

    async def _dismiss_approvals(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        message: str,
        trigger_type: str,
    ) -> bool:
        """Dismiss bot approvals on a PR.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name
            pr_number: PR number
            message: Dismissal message
            trigger_type: What triggered the dismissal (label_removed, chatops)
    
        Returns:
            True if successful
        """
        with create_span(
            "webhook.dismiss_approvals",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "dismissal.trigger_type": trigger_type,
            },
        ) as span:
            start_time = time.time()
    
            try:
                # Find all bot reviews
>               review_ids = await run_in_threadpool(
                    github_client.find_bot_reviews,
                    installation_id,
                    repo_full_name,
                    pr_number,
                )

stampbot/webhook_handler.py:772: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/concurrency.py:32: in run_in_threadpool
    return await anyio.to_thread.run_sync(func)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/to_thread.py:63: in run_sync
    return await get_async_backend().run_sync_in_worker_thread(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/_backends/_asyncio.py:2502: in run_sync_in_worker_thread
    return await future
           ^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/_backends/_asyncio.py:986: in run
    result = context.run(func, *args)
             ^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <MagicMock name='github_client.find_bot_reviews' id='140402676127392'>
args = (12345, 'octocat/hello-world', 42), kwargs = {}
effect = Exception('API error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fb20bf6fef0>
mock_github_client = <MagicMock name='github_client' id='140402676125712'>

    @pytest.mark.asyncio
    async def test_dismiss_approvals_exception(webhook_handler, mock_github_client):
        """Test _dismiss_approvals handles exceptions gracefully."""
        payload = load_fixture("pr_unlabeled_autoapprove")
    
        # Make find_bot_reviews raise an exception
        mock_github_client.find_bot_reviews.side_effect = Exception("API error")
    
>       result = await webhook_handler.handle_event("pull_request", payload)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_webhook_handler.py:727: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/webhook_handler.py:113: in handle_event
    result = await self._handle_pull_request(payload)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/webhook_handler.py:332: in _handle_pull_request
    success = await self._dismiss_approvals(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7fb20bf6fef0>
installation_id = 12345, repo_full_name = 'octocat/hello-world', pr_number = 42
message = 'Label autoapprove removed', trigger_type = 'label_removed'

    async def _dismiss_approvals(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        message: str,
        trigger_type: str,
    ) -> bool:
        """Dismiss bot approvals on a PR.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name
            pr_number: PR number
            message: Dismissal message
            trigger_type: What triggered the dismissal (label_removed, chatops)
    
        Returns:
            True if successful
        """
        with create_span(
            "webhook.dismiss_approvals",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "dismissal.trigger_type": trigger_type,
            },
        ) as span:
            start_time = time.time()
    
            try:
                # Find all bot reviews
                review_ids = await run_in_threadpool(
                    github_client.find_bot_reviews,
                    installation_id,
                    repo_full_name,
                    pr_number,
                )
    
                add_span_attributes(span, {"dismissal.reviews_found": len(review_ids)})
    
                if not review_ids:
                    logger.info(
                        "No bot approvals found on PR #%d",
                        pr_number,
                        extra={"repo": repo_full_name, "pr_number": pr_number},
                    )
                    duration = time.time() - start_time
                    pr_dismissal_duration_seconds.observe(duration)
                    pr_dismissals_total.labels(trigger_type=trigger_type, status="success").inc()
                    add_span_attributes(span, {"dismissal.result": "no_reviews"})
                    set_span_ok(span)
                    return True
    
                # Dismiss each review
                success = True
                for review_id in review_ids:
                    result = await run_in_threadpool(
                        github_client.dismiss_approval,
                        installation_id,
                        repo_full_name,
                        pr_number,
                        review_id,
                        message,
                    )
                    success = success and result
    
                duration = time.time() - start_time
                pr_dismissal_duration_seconds.observe(duration)
    
                status = "success" if success else "failure"
                pr_dismissals_total.labels(trigger_type=trigger_type, status=status).inc()
    
                add_span_attributes(span, {"dismissal.result": status})
                set_span_ok(span)
    
                return success
    
            except Exception as e:
>               duration = time.time() << start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for <<: 'float' and 'float'

stampbot/webhook_handler.py:819: TypeError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:43:04.825973Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-03-27T13:43:04.826542Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T13:43:04.827092Z [info     ] Approval label autoapprove removed from PR #42 _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'unlabeled'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:43:04.825973Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:43:04.826542Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:320 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'Approval label autoapprove removed from PR #42', 'level': 'info', 'timestamp': '2026-03-27T13:43:04.827092Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_dismiss_approvals_exception - TypeError: unsupported operand type(s) for <<: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 204 passed, 3 deselected in 1.80s
operator: core/ReplaceBinaryOperator_Sub_BitOr, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -720,7 +720,7 @@
                 comment,
             )
 
-            duration = time.time() - start_time
+            duration = time.time() | start_time
             pr_approval_duration_seconds.observe(duration)
 
             status = "success" if success else "failure"
........................................................................ [ 34%]
........................................................................ [ 68%]
...................F
=================================== FAILURES ===================================
____________________ test_pr_opened_with_autoapprove_label _____________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f20a6304050>
mock_github_client = <MagicMock name='github_client' id='139778204435520'>

    @pytest.mark.asyncio
    async def test_pr_opened_with_autoapprove_label(webhook_handler, mock_github_client):
        """Test PR opened with autoapprove label triggers approval."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
    
>       result = await webhook_handler.handle_event("pull_request", payload)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_webhook_handler.py:68: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/webhook_handler.py:113: in handle_event
    result = await self._handle_pull_request(payload)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/webhook_handler.py:290: in _handle_pull_request
    success = await self._approve_pr(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7f20a6304050>
installation_id = 12345, repo_full_name = 'octocat/hello-world', pr_number = 42
comment = 'Auto-approved by Stampbot (label: autoapprove)'
trigger_type = 'label'

    async def _approve_pr(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        comment: str,
        trigger_type: str,
    ) -> bool:
        """Approve a PR and track metrics.
    
        Checks for existing active approvals first to avoid duplicate comments.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name
            pr_number: PR number
            comment: Approval comment
            trigger_type: What triggered the approval (label, chatops)
    
        Returns:
            True if successful (or already approved)
        """
        with create_span(
            "webhook.approve_pr",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "approval.trigger_type": trigger_type,
            },
        ) as span:
            # Check for existing active approval to avoid duplicates
            existing_approvals = await run_in_threadpool(
                github_client.find_bot_reviews,
                installation_id,
                repo_full_name,
                pr_number,
            )
    
            if existing_approvals:
                logger.info(
                    "PR #%d in %s already has active approval, skipping",
                    pr_number,
                    repo_full_name,
                    extra={
                        "repo": repo_full_name,
                        "pr_number": pr_number,
                        "existing_review_ids": existing_approvals,
                    },
                )
                add_span_attributes(
                    span,
                    {
                        "approval.result": "already_approved",
                        "approval.existing_reviews": len(existing_approvals),
                    },
                )
                set_span_ok(span)
                return True
    
            start_time = time.time()
    
            success = await run_in_threadpool(
                github_client.approve_pr,
                installation_id,
                repo_full_name,
                pr_number,
                comment,
            )
    
>           duration = time.time() | start_time
                       ^^^^^^^^^^^^^^^^^^^^^^^^
E           TypeError: unsupported operand type(s) for |: 'float' and 'float'

stampbot/webhook_handler.py:723: TypeError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:47:33.842677Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T13:47:33.843414Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T13:47:33.844281Z [info     ] PR #42 has approval label: autoapprove _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:47:33.842677Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:47:33.843414Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:278 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'PR #42 has approval label: autoapprove', 'level': 'info', 'timestamp': '2026-03-27T13:47:33.844281Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_opened_with_autoapprove_label - TypeError: unsupported operand type(s) for |: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 163 passed, 3 deselected in 1.13s
operator: core/ReplaceBinaryOperator_Sub_BitOr, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -784,7 +784,7 @@
                         pr_number,
                         extra={"repo": repo_full_name, "pr_number": pr_number},
                     )
-                    duration = time.time() - start_time
+                    duration = time.time() | start_time
                     pr_dismissal_duration_seconds.observe(duration)
                     pr_dismissals_total.labels(trigger_type=trigger_type, status="success").inc()
                     add_span_attributes(span, {"dismissal.result": "no_reviews"})
........................................................................ [ 34%]
........................................................................ [ 68%]
...........................F
=================================== FAILURES ===================================
_______________________ test_pr_unlabeled_no_bot_reviews _______________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f0dd9aa55e0>
mock_github_client = <MagicMock name='github_client' id='139697458719312'>

    @pytest.mark.asyncio
    async def test_pr_unlabeled_no_bot_reviews(webhook_handler, mock_github_client):
        """Test removing label when no bot reviews exist."""
        payload = load_fixture("pr_unlabeled_autoapprove")
        mock_github_client.find_bot_reviews.return_value = []  # No reviews to dismiss
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'error' == 'success'
E         
E         - success
E         + error

tests/test_webhook_handler.py:179: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:03:06.413627Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-03-27T14:03:06.414195Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T14:03:06.414719Z [info     ] Approval label autoapprove removed from PR #42 _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
2026-03-27T14:03:06.415291Z [info     ] No bot approvals found on PR #42 _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42}
2026-03-27T14:03:06.415546Z [error    ] Error dismissing approvals: unsupported operand type(s) for |: 'float' and 'float' _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=error extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'error': "unsupported operand type(s) for |: 'float' and 'float'"}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'unlabeled'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T14:03:06.413627Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T14:03:06.414195Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:320 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'Approval label autoapprove removed from PR #42', 'level': 'info', 'timestamp': '2026-03-27T14:03:06.414719Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:782 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42}, 'event': 'No bot approvals found on PR #42', 'level': 'info', 'timestamp': '2026-03-27T14:03:06.415291Z'}
ERROR    stampbot.webhook_handler:webhook_handler.py:823 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'error': "unsupported operand type(s) for |: 'float' and 'float'"}, 'event': "Error dismissing approvals: unsupported operand type(s) for |: 'float' and 'float'", 'level': 'error', 'timestamp': '2026-03-27T14:03:06.415546Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_unlabeled_no_bot_reviews - AssertionError: assert 'error' == 'success'
  
  - success
  + error
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 171 passed, 3 deselected in 1.10s
operator: core/ReplaceBinaryOperator_Sub_BitOr, occurrence: 2
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -804,7 +804,7 @@
                     )
                     success = success and result
 
-                duration = time.time() - start_time
+                duration = time.time() | start_time
                 pr_dismissal_duration_seconds.observe(duration)
 
                 status = "success" if success else "failure"
........................................................................ [ 34%]
........................................................................ [ 68%]
.........................F
=================================== FAILURES ===================================
________________________ test_pr_unlabeled_autoapprove _________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f04ed7d9250>
mock_github_client = <MagicMock name='github_client' id='139659203108144'>

    @pytest.mark.asyncio
    async def test_pr_unlabeled_autoapprove(webhook_handler, mock_github_client):
        """Test removing autoapprove label dismisses approvals."""
        payload = load_fixture("pr_unlabeled_autoapprove")
        mock_github_client.find_bot_reviews.return_value = [111, 222]  # Mock review IDs
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'error' == 'success'
E         
E         - success
E         + error

tests/test_webhook_handler.py:152: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:11:50.365850Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-03-27T14:11:50.366376Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T14:11:50.366918Z [info     ] Approval label autoapprove removed from PR #42 _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
2026-03-27T14:11:50.367616Z [error    ] Error dismissing approvals: unsupported operand type(s) for |: 'float' and 'float' _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=error extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'error': "unsupported operand type(s) for |: 'float' and 'float'"}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'unlabeled'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T14:11:50.365850Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T14:11:50.366376Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:320 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'Approval label autoapprove removed from PR #42', 'level': 'info', 'timestamp': '2026-03-27T14:11:50.366918Z'}
ERROR    stampbot.webhook_handler:webhook_handler.py:823 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'error': "unsupported operand type(s) for |: 'float' and 'float'"}, 'event': "Error dismissing approvals: unsupported operand type(s) for |: 'float' and 'float'", 'level': 'error', 'timestamp': '2026-03-27T14:11:50.367616Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_unlabeled_autoapprove - AssertionError: assert 'error' == 'success'
  
  - success
  + error
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 169 passed, 3 deselected in 1.10s
operator: core/ReplaceBinaryOperator_Sub_BitOr, occurrence: 3
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -816,7 +816,7 @@
                 return success
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() | start_time
                 pr_dismissal_duration_seconds.observe(duration)
                 pr_dismissals_total.labels(trigger_type=trigger_type, status="failure").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
............................................................F
=================================== FAILURES ===================================
_______________________ test_dismiss_approvals_exception _______________________

self = <stampbot.webhook_handler.WebhookHandler object at 0x7fdd3fb77ef0>
installation_id = 12345, repo_full_name = 'octocat/hello-world', pr_number = 42
message = 'Label autoapprove removed', trigger_type = 'label_removed'

    async def _dismiss_approvals(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        message: str,
        trigger_type: str,
    ) -> bool:
        """Dismiss bot approvals on a PR.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name
            pr_number: PR number
            message: Dismissal message
            trigger_type: What triggered the dismissal (label_removed, chatops)
    
        Returns:
            True if successful
        """
        with create_span(
            "webhook.dismiss_approvals",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "dismissal.trigger_type": trigger_type,
            },
        ) as span:
            start_time = time.time()
    
            try:
                # Find all bot reviews
>               review_ids = await run_in_threadpool(
                    github_client.find_bot_reviews,
                    installation_id,
                    repo_full_name,
                    pr_number,
                )

stampbot/webhook_handler.py:772: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/concurrency.py:32: in run_in_threadpool
    return await anyio.to_thread.run_sync(func)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/to_thread.py:63: in run_sync
    return await get_async_backend().run_sync_in_worker_thread(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/_backends/_asyncio.py:2502: in run_sync_in_worker_thread
    return await future
           ^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/_backends/_asyncio.py:986: in run
    result = context.run(func, *args)
             ^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <MagicMock name='github_client.find_bot_reviews' id='140588227876512'>
args = (12345, 'octocat/hello-world', 42), kwargs = {}
effect = Exception('API error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fdd3fb77ef0>
mock_github_client = <MagicMock name='github_client' id='140588227874832'>

    @pytest.mark.asyncio
    async def test_dismiss_approvals_exception(webhook_handler, mock_github_client):
        """Test _dismiss_approvals handles exceptions gracefully."""
        payload = load_fixture("pr_unlabeled_autoapprove")
    
        # Make find_bot_reviews raise an exception
        mock_github_client.find_bot_reviews.side_effect = Exception("API error")
    
>       result = await webhook_handler.handle_event("pull_request", payload)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_webhook_handler.py:727: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/webhook_handler.py:113: in handle_event
    result = await self._handle_pull_request(payload)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/webhook_handler.py:332: in _handle_pull_request
    success = await self._dismiss_approvals(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7fdd3fb77ef0>
installation_id = 12345, repo_full_name = 'octocat/hello-world', pr_number = 42
message = 'Label autoapprove removed', trigger_type = 'label_removed'

    async def _dismiss_approvals(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        message: str,
        trigger_type: str,
    ) -> bool:
        """Dismiss bot approvals on a PR.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name
            pr_number: PR number
            message: Dismissal message
            trigger_type: What triggered the dismissal (label_removed, chatops)
    
        Returns:
            True if successful
        """
        with create_span(
            "webhook.dismiss_approvals",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "dismissal.trigger_type": trigger_type,
            },
        ) as span:
            start_time = time.time()
    
            try:
                # Find all bot reviews
                review_ids = await run_in_threadpool(
                    github_client.find_bot_reviews,
                    installation_id,
                    repo_full_name,
                    pr_number,
                )
    
                add_span_attributes(span, {"dismissal.reviews_found": len(review_ids)})
    
                if not review_ids:
                    logger.info(
                        "No bot approvals found on PR #%d",
                        pr_number,
                        extra={"repo": repo_full_name, "pr_number": pr_number},
                    )
                    duration = time.time() - start_time
                    pr_dismissal_duration_seconds.observe(duration)
                    pr_dismissals_total.labels(trigger_type=trigger_type, status="success").inc()
                    add_span_attributes(span, {"dismissal.result": "no_reviews"})
                    set_span_ok(span)
                    return True
    
                # Dismiss each review
                success = True
                for review_id in review_ids:
                    result = await run_in_threadpool(
                        github_client.dismiss_approval,
                        installation_id,
                        repo_full_name,
                        pr_number,
                        review_id,
                        message,
                    )
                    success = success and result
    
                duration = time.time() - start_time
                pr_dismissal_duration_seconds.observe(duration)
    
                status = "success" if success else "failure"
                pr_dismissals_total.labels(trigger_type=trigger_type, status=status).inc()
    
                add_span_attributes(span, {"dismissal.result": status})
                set_span_ok(span)
    
                return success
    
            except Exception as e:
>               duration = time.time() | start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for |: 'float' and 'float'

stampbot/webhook_handler.py:819: TypeError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:57:02.043995Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-03-27T13:57:02.044556Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T13:57:02.045127Z [info     ] Approval label autoapprove removed from PR #42 _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'unlabeled'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:57:02.043995Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:57:02.044556Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:320 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'Approval label autoapprove removed from PR #42', 'level': 'info', 'timestamp': '2026-03-27T13:57:02.045127Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_dismiss_approvals_exception - TypeError: unsupported operand type(s) for |: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 204 passed, 3 deselected in 1.82s
operator: core/ReplaceBinaryOperator_Sub_BitAnd, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -720,7 +720,7 @@
                 comment,
             )
 
-            duration = time.time() - start_time
+            duration = time.time() & start_time
             pr_approval_duration_seconds.observe(duration)
 
             status = "success" if success else "failure"
........................................................................ [ 34%]
........................................................................ [ 68%]
...................F
=================================== FAILURES ===================================
____________________ test_pr_opened_with_autoapprove_label _____________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f06f2800050>
mock_github_client = <MagicMock name='github_client' id='139667815580736'>

    @pytest.mark.asyncio
    async def test_pr_opened_with_autoapprove_label(webhook_handler, mock_github_client):
        """Test PR opened with autoapprove label triggers approval."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
    
>       result = await webhook_handler.handle_event("pull_request", payload)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_webhook_handler.py:68: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/webhook_handler.py:113: in handle_event
    result = await self._handle_pull_request(payload)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/webhook_handler.py:290: in _handle_pull_request
    success = await self._approve_pr(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7f06f2800050>
installation_id = 12345, repo_full_name = 'octocat/hello-world', pr_number = 42
comment = 'Auto-approved by Stampbot (label: autoapprove)'
trigger_type = 'label'

    async def _approve_pr(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        comment: str,
        trigger_type: str,
    ) -> bool:
        """Approve a PR and track metrics.
    
        Checks for existing active approvals first to avoid duplicate comments.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name
            pr_number: PR number
            comment: Approval comment
            trigger_type: What triggered the approval (label, chatops)
    
        Returns:
            True if successful (or already approved)
        """
        with create_span(
            "webhook.approve_pr",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "approval.trigger_type": trigger_type,
            },
        ) as span:
            # Check for existing active approval to avoid duplicates
            existing_approvals = await run_in_threadpool(
                github_client.find_bot_reviews,
                installation_id,
                repo_full_name,
                pr_number,
            )
    
            if existing_approvals:
                logger.info(
                    "PR #%d in %s already has active approval, skipping",
                    pr_number,
                    repo_full_name,
                    extra={
                        "repo": repo_full_name,
                        "pr_number": pr_number,
                        "existing_review_ids": existing_approvals,
                    },
                )
                add_span_attributes(
                    span,
                    {
                        "approval.result": "already_approved",
                        "approval.existing_reviews": len(existing_approvals),
                    },
                )
                set_span_ok(span)
                return True
    
            start_time = time.time()
    
            success = await run_in_threadpool(
                github_client.approve_pr,
                installation_id,
                repo_full_name,
                pr_number,
                comment,
            )
    
>           duration = time.time() & start_time
                       ^^^^^^^^^^^^^^^^^^^^^^^^
E           TypeError: unsupported operand type(s) for &: 'float' and 'float'

stampbot/webhook_handler.py:723: TypeError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:41:25.444204Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T13:41:25.444858Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T13:41:25.445710Z [info     ] PR #42 has approval label: autoapprove _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:41:25.444204Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:41:25.444858Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:278 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'PR #42 has approval label: autoapprove', 'level': 'info', 'timestamp': '2026-03-27T13:41:25.445710Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_opened_with_autoapprove_label - TypeError: unsupported operand type(s) for &: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 163 passed, 3 deselected in 1.10s
operator: core/ReplaceBinaryOperator_Sub_BitAnd, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -784,7 +784,7 @@
                         pr_number,
                         extra={"repo": repo_full_name, "pr_number": pr_number},
                     )
-                    duration = time.time() - start_time
+                    duration = time.time() & start_time
                     pr_dismissal_duration_seconds.observe(duration)
                     pr_dismissals_total.labels(trigger_type=trigger_type, status="success").inc()
                     add_span_attributes(span, {"dismissal.result": "no_reviews"})
........................................................................ [ 34%]
........................................................................ [ 68%]
...........................F
=================================== FAILURES ===================================
_______________________ test_pr_unlabeled_no_bot_reviews _______________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7efee2d895e0>
mock_github_client = <MagicMock name='github_client' id='139633188235856'>

    @pytest.mark.asyncio
    async def test_pr_unlabeled_no_bot_reviews(webhook_handler, mock_github_client):
        """Test removing label when no bot reviews exist."""
        payload = load_fixture("pr_unlabeled_autoapprove")
        mock_github_client.find_bot_reviews.return_value = []  # No reviews to dismiss
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'error' == 'success'
E         
E         - success
E         + error

tests/test_webhook_handler.py:179: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:05:59.086372Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-03-27T14:05:59.086930Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T14:05:59.087441Z [info     ] Approval label autoapprove removed from PR #42 _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
2026-03-27T14:05:59.087862Z [info     ] No bot approvals found on PR #42 _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42}
2026-03-27T14:05:59.088064Z [error    ] Error dismissing approvals: unsupported operand type(s) for &: 'float' and 'float' _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=error extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'error': "unsupported operand type(s) for &: 'float' and 'float'"}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'unlabeled'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T14:05:59.086372Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T14:05:59.086930Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:320 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'Approval label autoapprove removed from PR #42', 'level': 'info', 'timestamp': '2026-03-27T14:05:59.087441Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:782 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42}, 'event': 'No bot approvals found on PR #42', 'level': 'info', 'timestamp': '2026-03-27T14:05:59.087862Z'}
ERROR    stampbot.webhook_handler:webhook_handler.py:823 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'error': "unsupported operand type(s) for &: 'float' and 'float'"}, 'event': "Error dismissing approvals: unsupported operand type(s) for &: 'float' and 'float'", 'level': 'error', 'timestamp': '2026-03-27T14:05:59.088064Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_unlabeled_no_bot_reviews - AssertionError: assert 'error' == 'success'
  
  - success
  + error
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 171 passed, 3 deselected in 1.09s
operator: core/ReplaceBinaryOperator_Sub_BitAnd, occurrence: 2
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -804,7 +804,7 @@
                     )
                     success = success and result
 
-                duration = time.time() - start_time
+                duration = time.time() & start_time
                 pr_dismissal_duration_seconds.observe(duration)
 
                 status = "success" if success else "failure"
........................................................................ [ 34%]
........................................................................ [ 68%]
.........................F
=================================== FAILURES ===================================
________________________ test_pr_unlabeled_autoapprove _________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fbcb73e5650>
mock_github_client = <MagicMock name='github_client' id='140448566943024'>

    @pytest.mark.asyncio
    async def test_pr_unlabeled_autoapprove(webhook_handler, mock_github_client):
        """Test removing autoapprove label dismisses approvals."""
        payload = load_fixture("pr_unlabeled_autoapprove")
        mock_github_client.find_bot_reviews.return_value = [111, 222]  # Mock review IDs
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'error' == 'success'
E         
E         - success
E         + error

tests/test_webhook_handler.py:152: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:29:51.613265Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-03-27T13:29:51.613810Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T13:29:51.614333Z [info     ] Approval label autoapprove removed from PR #42 _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
2026-03-27T13:29:51.615039Z [error    ] Error dismissing approvals: unsupported operand type(s) for &: 'float' and 'float' _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=error extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'error': "unsupported operand type(s) for &: 'float' and 'float'"}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'unlabeled'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:29:51.613265Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:29:51.613810Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:320 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'Approval label autoapprove removed from PR #42', 'level': 'info', 'timestamp': '2026-03-27T13:29:51.614333Z'}
ERROR    stampbot.webhook_handler:webhook_handler.py:823 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'error': "unsupported operand type(s) for &: 'float' and 'float'"}, 'event': "Error dismissing approvals: unsupported operand type(s) for &: 'float' and 'float'", 'level': 'error', 'timestamp': '2026-03-27T13:29:51.615039Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_unlabeled_autoapprove - AssertionError: assert 'error' == 'success'
  
  - success
  + error
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 169 passed, 3 deselected in 1.10s
operator: core/ReplaceBinaryOperator_Sub_BitAnd, occurrence: 3
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -816,7 +816,7 @@
                 return success
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() & start_time
                 pr_dismissal_duration_seconds.observe(duration)
                 pr_dismissals_total.labels(trigger_type=trigger_type, status="failure").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
............................................................F
=================================== FAILURES ===================================
_______________________ test_dismiss_approvals_exception _______________________

self = <stampbot.webhook_handler.WebhookHandler object at 0x7fb7eecc7e90>
installation_id = 12345, repo_full_name = 'octocat/hello-world', pr_number = 42
message = 'Label autoapprove removed', trigger_type = 'label_removed'

    async def _dismiss_approvals(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        message: str,
        trigger_type: str,
    ) -> bool:
        """Dismiss bot approvals on a PR.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name
            pr_number: PR number
            message: Dismissal message
            trigger_type: What triggered the dismissal (label_removed, chatops)
    
        Returns:
            True if successful
        """
        with create_span(
            "webhook.dismiss_approvals",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "dismissal.trigger_type": trigger_type,
            },
        ) as span:
            start_time = time.time()
    
            try:
                # Find all bot reviews
>               review_ids = await run_in_threadpool(
                    github_client.find_bot_reviews,
                    installation_id,
                    repo_full_name,
                    pr_number,
                )

stampbot/webhook_handler.py:772: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/concurrency.py:32: in run_in_threadpool
    return await anyio.to_thread.run_sync(func)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/to_thread.py:63: in run_sync
    return await get_async_backend().run_sync_in_worker_thread(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/_backends/_asyncio.py:2502: in run_sync_in_worker_thread
    return await future
           ^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/_backends/_asyncio.py:986: in run
    result = context.run(func, *args)
             ^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <MagicMock name='github_client.find_bot_reviews' id='140427961538208'>
args = (12345, 'octocat/hello-world', 42), kwargs = {}
effect = Exception('API error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fb7eecc7e90>
mock_github_client = <MagicMock name='github_client' id='140427961536528'>

    @pytest.mark.asyncio
    async def test_dismiss_approvals_exception(webhook_handler, mock_github_client):
        """Test _dismiss_approvals handles exceptions gracefully."""
        payload = load_fixture("pr_unlabeled_autoapprove")
    
        # Make find_bot_reviews raise an exception
        mock_github_client.find_bot_reviews.side_effect = Exception("API error")
    
>       result = await webhook_handler.handle_event("pull_request", payload)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_webhook_handler.py:727: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/webhook_handler.py:113: in handle_event
    result = await self._handle_pull_request(payload)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/webhook_handler.py:332: in _handle_pull_request
    success = await self._dismiss_approvals(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7fb7eecc7e90>
installation_id = 12345, repo_full_name = 'octocat/hello-world', pr_number = 42
message = 'Label autoapprove removed', trigger_type = 'label_removed'

    async def _dismiss_approvals(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        message: str,
        trigger_type: str,
    ) -> bool:
        """Dismiss bot approvals on a PR.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name
            pr_number: PR number
            message: Dismissal message
            trigger_type: What triggered the dismissal (label_removed, chatops)
    
        Returns:
            True if successful
        """
        with create_span(
            "webhook.dismiss_approvals",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "dismissal.trigger_type": trigger_type,
            },
        ) as span:
            start_time = time.time()
    
            try:
                # Find all bot reviews
                review_ids = await run_in_threadpool(
                    github_client.find_bot_reviews,
                    installation_id,
                    repo_full_name,
                    pr_number,
                )
    
                add_span_attributes(span, {"dismissal.reviews_found": len(review_ids)})
    
                if not review_ids:
                    logger.info(
                        "No bot approvals found on PR #%d",
                        pr_number,
                        extra={"repo": repo_full_name, "pr_number": pr_number},
                    )
                    duration = time.time() - start_time
                    pr_dismissal_duration_seconds.observe(duration)
                    pr_dismissals_total.labels(trigger_type=trigger_type, status="success").inc()
                    add_span_attributes(span, {"dismissal.result": "no_reviews"})
                    set_span_ok(span)
                    return True
    
                # Dismiss each review
                success = True
                for review_id in review_ids:
                    result = await run_in_threadpool(
                        github_client.dismiss_approval,
                        installation_id,
                        repo_full_name,
                        pr_number,
                        review_id,
                        message,
                    )
                    success = success and result
    
                duration = time.time() - start_time
                pr_dismissal_duration_seconds.observe(duration)
    
                status = "success" if success else "failure"
                pr_dismissals_total.labels(trigger_type=trigger_type, status=status).inc()
    
                add_span_attributes(span, {"dismissal.result": status})
                set_span_ok(span)
    
                return success
    
            except Exception as e:
>               duration = time.time() & start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for &: 'float' and 'float'

stampbot/webhook_handler.py:819: TypeError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:48:27.199245Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-03-27T13:48:27.199816Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T13:48:27.200339Z [info     ] Approval label autoapprove removed from PR #42 _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'unlabeled'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:48:27.199245Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:48:27.199816Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:320 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'Approval label autoapprove removed from PR #42', 'level': 'info', 'timestamp': '2026-03-27T13:48:27.200339Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_dismiss_approvals_exception - TypeError: unsupported operand type(s) for &: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 204 passed, 3 deselected in 1.82s
operator: core/ReplaceBinaryOperator_Sub_BitXor, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -720,7 +720,7 @@
                 comment,
             )
 
-            duration = time.time() - start_time
+            duration = time.time() ^ start_time
             pr_approval_duration_seconds.observe(duration)
 
             status = "success" if success else "failure"
........................................................................ [ 34%]
........................................................................ [ 68%]
...................F
=================================== FAILURES ===================================
____________________ test_pr_opened_with_autoapprove_label _____________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7efdbf7ec050>
mock_github_client = <MagicMock name='github_client' id='139628303074368'>

    @pytest.mark.asyncio
    async def test_pr_opened_with_autoapprove_label(webhook_handler, mock_github_client):
        """Test PR opened with autoapprove label triggers approval."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
    
>       result = await webhook_handler.handle_event("pull_request", payload)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_webhook_handler.py:68: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/webhook_handler.py:113: in handle_event
    result = await self._handle_pull_request(payload)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/webhook_handler.py:290: in _handle_pull_request
    success = await self._approve_pr(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7efdbf7ec050>
installation_id = 12345, repo_full_name = 'octocat/hello-world', pr_number = 42
comment = 'Auto-approved by Stampbot (label: autoapprove)'
trigger_type = 'label'

    async def _approve_pr(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        comment: str,
        trigger_type: str,
    ) -> bool:
        """Approve a PR and track metrics.
    
        Checks for existing active approvals first to avoid duplicate comments.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name
            pr_number: PR number
            comment: Approval comment
            trigger_type: What triggered the approval (label, chatops)
    
        Returns:
            True if successful (or already approved)
        """
        with create_span(
            "webhook.approve_pr",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "approval.trigger_type": trigger_type,
            },
        ) as span:
            # Check for existing active approval to avoid duplicates
            existing_approvals = await run_in_threadpool(
                github_client.find_bot_reviews,
                installation_id,
                repo_full_name,
                pr_number,
            )
    
            if existing_approvals:
                logger.info(
                    "PR #%d in %s already has active approval, skipping",
                    pr_number,
                    repo_full_name,
                    extra={
                        "repo": repo_full_name,
                        "pr_number": pr_number,
                        "existing_review_ids": existing_approvals,
                    },
                )
                add_span_attributes(
                    span,
                    {
                        "approval.result": "already_approved",
                        "approval.existing_reviews": len(existing_approvals),
                    },
                )
                set_span_ok(span)
                return True
    
            start_time = time.time()
    
            success = await run_in_threadpool(
                github_client.approve_pr,
                installation_id,
                repo_full_name,
                pr_number,
                comment,
            )
    
>           duration = time.time() ^ start_time
                       ^^^^^^^^^^^^^^^^^^^^^^^^
E           TypeError: unsupported operand type(s) for ^: 'float' and 'float'

stampbot/webhook_handler.py:723: TypeError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:58:31.142331Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T13:58:31.143025Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T13:58:31.143874Z [info     ] PR #42 has approval label: autoapprove _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:58:31.142331Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:58:31.143025Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:278 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'PR #42 has approval label: autoapprove', 'level': 'info', 'timestamp': '2026-03-27T13:58:31.143874Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_opened_with_autoapprove_label - TypeError: unsupported operand type(s) for ^: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 163 passed, 3 deselected in 1.12s
operator: core/ReplaceBinaryOperator_Sub_BitXor, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -784,7 +784,7 @@
                         pr_number,
                         extra={"repo": repo_full_name, "pr_number": pr_number},
                     )
-                    duration = time.time() - start_time
+                    duration = time.time() ^ start_time
                     pr_dismissal_duration_seconds.observe(duration)
                     pr_dismissals_total.labels(trigger_type=trigger_type, status="success").inc()
                     add_span_attributes(span, {"dismissal.result": "no_reviews"})
........................................................................ [ 34%]
........................................................................ [ 68%]
...........................F
=================================== FAILURES ===================================
_______________________ test_pr_unlabeled_no_bot_reviews _______________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f0ada3a95e0>
mock_github_client = <MagicMock name='github_client' id='139684583270992'>

    @pytest.mark.asyncio
    async def test_pr_unlabeled_no_bot_reviews(webhook_handler, mock_github_client):
        """Test removing label when no bot reviews exist."""
        payload = load_fixture("pr_unlabeled_autoapprove")
        mock_github_client.find_bot_reviews.return_value = []  # No reviews to dismiss
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'error' == 'success'
E         
E         - success
E         + error

tests/test_webhook_handler.py:179: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:11:55.317865Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-03-27T14:11:55.318403Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T14:11:55.318937Z [info     ] Approval label autoapprove removed from PR #42 _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
2026-03-27T14:11:55.319373Z [info     ] No bot approvals found on PR #42 _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42}
2026-03-27T14:11:55.319573Z [error    ] Error dismissing approvals: unsupported operand type(s) for ^: 'float' and 'float' _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=error extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'error': "unsupported operand type(s) for ^: 'float' and 'float'"}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'unlabeled'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T14:11:55.317865Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T14:11:55.318403Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:320 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'Approval label autoapprove removed from PR #42', 'level': 'info', 'timestamp': '2026-03-27T14:11:55.318937Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:782 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42}, 'event': 'No bot approvals found on PR #42', 'level': 'info', 'timestamp': '2026-03-27T14:11:55.319373Z'}
ERROR    stampbot.webhook_handler:webhook_handler.py:823 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'error': "unsupported operand type(s) for ^: 'float' and 'float'"}, 'event': "Error dismissing approvals: unsupported operand type(s) for ^: 'float' and 'float'", 'level': 'error', 'timestamp': '2026-03-27T14:11:55.319573Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_unlabeled_no_bot_reviews - AssertionError: assert 'error' == 'success'
  
  - success
  + error
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 171 passed, 3 deselected in 1.11s
operator: core/ReplaceBinaryOperator_Sub_BitXor, occurrence: 2
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -804,7 +804,7 @@
                     )
                     success = success and result
 
-                duration = time.time() - start_time
+                duration = time.time() ^ start_time
                 pr_dismissal_duration_seconds.observe(duration)
 
                 status = "success" if success else "failure"
........................................................................ [ 34%]
........................................................................ [ 68%]
.........................F
=================================== FAILURES ===================================
________________________ test_pr_unlabeled_autoapprove _________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f5775fe1550>
mock_github_client = <MagicMock name='github_client' id='140013680598320'>

    @pytest.mark.asyncio
    async def test_pr_unlabeled_autoapprove(webhook_handler, mock_github_client):
        """Test removing autoapprove label dismisses approvals."""
        payload = load_fixture("pr_unlabeled_autoapprove")
        mock_github_client.find_bot_reviews.return_value = [111, 222]  # Mock review IDs
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'error' == 'success'
E         
E         - success
E         + error

tests/test_webhook_handler.py:152: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:09:08.175150Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-03-27T14:09:08.175672Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T14:09:08.176203Z [info     ] Approval label autoapprove removed from PR #42 _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
2026-03-27T14:09:08.176889Z [error    ] Error dismissing approvals: unsupported operand type(s) for ^: 'float' and 'float' _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=error extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'error': "unsupported operand type(s) for ^: 'float' and 'float'"}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'unlabeled'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T14:09:08.175150Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T14:09:08.175672Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:320 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'Approval label autoapprove removed from PR #42', 'level': 'info', 'timestamp': '2026-03-27T14:09:08.176203Z'}
ERROR    stampbot.webhook_handler:webhook_handler.py:823 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'error': "unsupported operand type(s) for ^: 'float' and 'float'"}, 'event': "Error dismissing approvals: unsupported operand type(s) for ^: 'float' and 'float'", 'level': 'error', 'timestamp': '2026-03-27T14:09:08.176889Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_unlabeled_autoapprove - AssertionError: assert 'error' == 'success'
  
  - success
  + error
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 169 passed, 3 deselected in 1.10s
operator: core/ReplaceBinaryOperator_Sub_BitXor, occurrence: 3
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -816,7 +816,7 @@
                 return success
 
             except Exception as e:
-                duration = time.time() - start_time
+                duration = time.time() ^ start_time
                 pr_dismissal_duration_seconds.observe(duration)
                 pr_dismissals_total.labels(trigger_type=trigger_type, status="failure").inc()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
............................................................F
=================================== FAILURES ===================================
_______________________ test_dismiss_approvals_exception _______________________

self = <stampbot.webhook_handler.WebhookHandler object at 0x7fdc45ea01d0>
installation_id = 12345, repo_full_name = 'octocat/hello-world', pr_number = 42
message = 'Label autoapprove removed', trigger_type = 'label_removed'

    async def _dismiss_approvals(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        message: str,
        trigger_type: str,
    ) -> bool:
        """Dismiss bot approvals on a PR.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name
            pr_number: PR number
            message: Dismissal message
            trigger_type: What triggered the dismissal (label_removed, chatops)
    
        Returns:
            True if successful
        """
        with create_span(
            "webhook.dismiss_approvals",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "dismissal.trigger_type": trigger_type,
            },
        ) as span:
            start_time = time.time()
    
            try:
                # Find all bot reviews
>               review_ids = await run_in_threadpool(
                    github_client.find_bot_reviews,
                    installation_id,
                    repo_full_name,
                    pr_number,
                )

stampbot/webhook_handler.py:772: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/concurrency.py:32: in run_in_threadpool
    return await anyio.to_thread.run_sync(func)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/to_thread.py:63: in run_sync
    return await get_async_backend().run_sync_in_worker_thread(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/_backends/_asyncio.py:2502: in run_sync_in_worker_thread
    return await future
           ^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/_backends/_asyncio.py:986: in run
    result = context.run(func, *args)
             ^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <MagicMock name='github_client.find_bot_reviews' id='140584036652704'>
args = (12345, 'octocat/hello-world', 42), kwargs = {}
effect = Exception('API error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fdc45ea01d0>
mock_github_client = <MagicMock name='github_client' id='140584036651024'>

    @pytest.mark.asyncio
    async def test_dismiss_approvals_exception(webhook_handler, mock_github_client):
        """Test _dismiss_approvals handles exceptions gracefully."""
        payload = load_fixture("pr_unlabeled_autoapprove")
    
        # Make find_bot_reviews raise an exception
        mock_github_client.find_bot_reviews.side_effect = Exception("API error")
    
>       result = await webhook_handler.handle_event("pull_request", payload)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_webhook_handler.py:727: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/webhook_handler.py:113: in handle_event
    result = await self._handle_pull_request(payload)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/webhook_handler.py:332: in _handle_pull_request
    success = await self._dismiss_approvals(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7fdc45ea01d0>
installation_id = 12345, repo_full_name = 'octocat/hello-world', pr_number = 42
message = 'Label autoapprove removed', trigger_type = 'label_removed'

    async def _dismiss_approvals(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        message: str,
        trigger_type: str,
    ) -> bool:
        """Dismiss bot approvals on a PR.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name
            pr_number: PR number
            message: Dismissal message
            trigger_type: What triggered the dismissal (label_removed, chatops)
    
        Returns:
            True if successful
        """
        with create_span(
            "webhook.dismiss_approvals",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "dismissal.trigger_type": trigger_type,
            },
        ) as span:
            start_time = time.time()
    
            try:
                # Find all bot reviews
                review_ids = await run_in_threadpool(
                    github_client.find_bot_reviews,
                    installation_id,
                    repo_full_name,
                    pr_number,
                )
    
                add_span_attributes(span, {"dismissal.reviews_found": len(review_ids)})
    
                if not review_ids:
                    logger.info(
                        "No bot approvals found on PR #%d",
                        pr_number,
                        extra={"repo": repo_full_name, "pr_number": pr_number},
                    )
                    duration = time.time() - start_time
                    pr_dismissal_duration_seconds.observe(duration)
                    pr_dismissals_total.labels(trigger_type=trigger_type, status="success").inc()
                    add_span_attributes(span, {"dismissal.result": "no_reviews"})
                    set_span_ok(span)
                    return True
    
                # Dismiss each review
                success = True
                for review_id in review_ids:
                    result = await run_in_threadpool(
                        github_client.dismiss_approval,
                        installation_id,
                        repo_full_name,
                        pr_number,
                        review_id,
                        message,
                    )
                    success = success and result
    
                duration = time.time() - start_time
                pr_dismissal_duration_seconds.observe(duration)
    
                status = "success" if success else "failure"
                pr_dismissals_total.labels(trigger_type=trigger_type, status=status).inc()
    
                add_span_attributes(span, {"dismissal.result": status})
                set_span_ok(span)
    
                return success
    
            except Exception as e:
>               duration = time.time() ^ start_time
                           ^^^^^^^^^^^^^^^^^^^^^^^^
E               TypeError: unsupported operand type(s) for ^: 'float' and 'float'

stampbot/webhook_handler.py:819: TypeError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:28:13.585365Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-03-27T13:28:13.585945Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T13:28:13.586475Z [info     ] Approval label autoapprove removed from PR #42 _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'unlabeled'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:28:13.585365Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:28:13.585945Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:320 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'Approval label autoapprove removed from PR #42', 'level': 'info', 'timestamp': '2026-03-27T13:28:13.586475Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_dismiss_approvals_exception - TypeError: unsupported operand type(s) for ^: 'float' and 'float'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 204 passed, 3 deselected in 1.80s
operator: core/ReplaceBinaryOperator_BitOr_Add, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -536,7 +536,7 @@
         installation_id: int,
         repo_full_name: str,
         default_branch: str,
-        owner_login: str | None,
+        owner_login: str + None,
         owner_type: str | None,
     ) -> RepoConfig:
         """Get repository configuration from stampbot.toml.
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_Add, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -537,7 +537,7 @@
         repo_full_name: str,
         default_branch: str,
         owner_login: str | None,
-        owner_type: str | None,
+        owner_type: str + None,
     ) -> RepoConfig:
         """Get repository configuration from stampbot.toml.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Sub, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -536,7 +536,7 @@
         installation_id: int,
         repo_full_name: str,
         default_branch: str,
-        owner_login: str | None,
+        owner_login: str - None,
         owner_type: str | None,
     ) -> RepoConfig:
         """Get repository configuration from stampbot.toml.
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Sub, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -537,7 +537,7 @@
         repo_full_name: str,
         default_branch: str,
         owner_login: str | None,
-        owner_type: str | None,
+        owner_type: str - None,
     ) -> RepoConfig:
         """Get repository configuration from stampbot.toml.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Mul, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -536,7 +536,7 @@
         installation_id: int,
         repo_full_name: str,
         default_branch: str,
-        owner_login: str | None,
+        owner_login: str * None,
         owner_type: str | None,
     ) -> RepoConfig:
         """Get repository configuration from stampbot.toml.
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Mul, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -537,7 +537,7 @@
         repo_full_name: str,
         default_branch: str,
         owner_login: str | None,
-        owner_type: str | None,
+        owner_type: str * None,
     ) -> RepoConfig:
         """Get repository configuration from stampbot.toml.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_Div, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -536,7 +536,7 @@
         installation_id: int,
         repo_full_name: str,
         default_branch: str,
-        owner_login: str | None,
+        owner_login: str / None,
         owner_type: str | None,
     ) -> RepoConfig:
         """Get repository configuration from stampbot.toml.
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceBinaryOperator_BitOr_Div, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -537,7 +537,7 @@
         repo_full_name: str,
         default_branch: str,
         owner_login: str | None,
-        owner_type: str | None,
+        owner_type: str / None,
     ) -> RepoConfig:
         """Get repository configuration from stampbot.toml.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceBinaryOperator_BitOr_FloorDiv, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -536,7 +536,7 @@
         installation_id: int,
         repo_full_name: str,
         default_branch: str,
-        owner_login: str | None,
+        owner_login: str // None,
         owner_type: str | None,
     ) -> RepoConfig:
         """Get repository configuration from stampbot.toml.
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_FloorDiv, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -537,7 +537,7 @@
         repo_full_name: str,
         default_branch: str,
         owner_login: str | None,
-        owner_type: str | None,
+        owner_type: str // None,
     ) -> RepoConfig:
         """Get repository configuration from stampbot.toml.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Mod, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -536,7 +536,7 @@
         installation_id: int,
         repo_full_name: str,
         default_branch: str,
-        owner_login: str | None,
+        owner_login: str % None,
         owner_type: str | None,
     ) -> RepoConfig:
         """Get repository configuration from stampbot.toml.
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_Mod, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -537,7 +537,7 @@
         repo_full_name: str,
         default_branch: str,
         owner_login: str | None,
-        owner_type: str | None,
+        owner_type: str % None,
     ) -> RepoConfig:
         """Get repository configuration from stampbot.toml.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_Pow, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -536,7 +536,7 @@
         installation_id: int,
         repo_full_name: str,
         default_branch: str,
-        owner_login: str | None,
+        owner_login: str ** None,
         owner_type: str | None,
     ) -> RepoConfig:
         """Get repository configuration from stampbot.toml.
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/ReplaceBinaryOperator_BitOr_Pow, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -537,7 +537,7 @@
         repo_full_name: str,
         default_branch: str,
         owner_login: str | None,
-        owner_type: str | None,
+        owner_type: str ** None,
     ) -> RepoConfig:
         """Get repository configuration from stampbot.toml.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.65s
operator: core/ReplaceBinaryOperator_BitOr_RShift, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -536,7 +536,7 @@
         installation_id: int,
         repo_full_name: str,
         default_branch: str,
-        owner_login: str | None,
+        owner_login: str >> None,
         owner_type: str | None,
     ) -> RepoConfig:
         """Get repository configuration from stampbot.toml.
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_RShift, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -537,7 +537,7 @@
         repo_full_name: str,
         default_branch: str,
         owner_login: str | None,
-        owner_type: str | None,
+        owner_type: str >> None,
     ) -> RepoConfig:
         """Get repository configuration from stampbot.toml.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_LShift, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -536,7 +536,7 @@
         installation_id: int,
         repo_full_name: str,
         default_branch: str,
-        owner_login: str | None,
+        owner_login: str << None,
         owner_type: str | None,
     ) -> RepoConfig:
         """Get repository configuration from stampbot.toml.
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_LShift, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -537,7 +537,7 @@
         repo_full_name: str,
         default_branch: str,
         owner_login: str | None,
-        owner_type: str | None,
+        owner_type: str << None,
     ) -> RepoConfig:
         """Get repository configuration from stampbot.toml.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_BitAnd, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -536,7 +536,7 @@
         installation_id: int,
         repo_full_name: str,
         default_branch: str,
-        owner_login: str | None,
+        owner_login: str & None,
         owner_type: str | None,
     ) -> RepoConfig:
         """Get repository configuration from stampbot.toml.
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceBinaryOperator_BitOr_BitAnd, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -537,7 +537,7 @@
         repo_full_name: str,
         default_branch: str,
         owner_login: str | None,
-        owner_type: str | None,
+        owner_type: str & None,
     ) -> RepoConfig:
         """Get repository configuration from stampbot.toml.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/ReplaceBinaryOperator_BitOr_BitXor, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -536,7 +536,7 @@
         installation_id: int,
         repo_full_name: str,
         default_branch: str,
-        owner_login: str | None,
+        owner_login: str ^ None,
         owner_type: str | None,
     ) -> RepoConfig:
         """Get repository configuration from stampbot.toml.
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceBinaryOperator_BitOr_BitXor, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -537,7 +537,7 @@
         repo_full_name: str,
         default_branch: str,
         owner_login: str | None,
-        owner_type: str | None,
+        owner_type: str ^ None,
     ) -> RepoConfig:
         """Get repository configuration from stampbot.toml.
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceComparisonOperator_Eq_NotEq, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -109,7 +109,7 @@
             )
 
             # Route to appropriate handler
-            if event_type == "pull_request":
+            if event_type != "pull_request":
                 result = await self._handle_pull_request(payload)
             elif event_type == "pull_request_review_comment":
                 result = await self._handle_pr_comment(payload)
........................................................................ [ 34%]
.............................F
=================================== FAILURES ===================================
___________________________ test_webhook_valid_ping ____________________________

test_client = <starlette.testclient.TestClient object at 0x7f017e9d68b0>

    def test_webhook_valid_ping(test_client: TestClient):
        """Test webhook accepts valid ping event."""
        import hashlib
        import hmac
    
        from stampbot.webhook_handler import webhook_handler
    
        body = json.dumps({"zen": "Design for failure."}).encode()
        signature = (
            "sha256="
            + hmac.new(
                webhook_handler.webhook_secret,
                body,
                hashlib.sha256,
            ).hexdigest()
        )
    
        response = test_client.post(
            "/webhook",
            content=body,
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": signature,
            },
        )
        assert response.status_code == 200
        data = response.json()
>       assert data["status"] == "ok"
E       AssertionError: assert 'error' == 'ok'
E         
E         - ok
E         + error

tests/test_main.py:177: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:45:30.685372Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-03-27T13:45:30.685585Z [warning  ] Missing required fields in PR event _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=warning client_ip=testclient
2026-03-27T13:45:30.686391Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 200 OK"
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'ping', 'action': ''}, 'event': 'Received ping event', 'client_ip': 'testclient', 'level': 'info', 'timestamp': '2026-03-27T13:45:30.685372Z'}
WARNING  stampbot.webhook_handler:webhook_handler.py:161 {'event': 'Missing required fields in PR event', 'client_ip': 'testclient', 'level': 'warning', 'timestamp': '2026-03-27T13:45:30.685585Z'}
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 200 OK"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_valid_ping - AssertionError: assert 'error' == 'ok'
  
  - ok
  + error
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 101 passed, 3 deselected in 0.85s
operator: core/ReplaceComparisonOperator_Eq_NotEq, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -111,7 +111,7 @@
             # Route to appropriate handler
             if event_type == "pull_request":
                 result = await self._handle_pull_request(payload)
-            elif event_type == "pull_request_review_comment":
+            elif event_type != "pull_request_review_comment":
                 result = await self._handle_pr_comment(payload)
             elif event_type == "issue_comment":
                 # Issue comments can be on PRs too
........................................................................ [ 34%]
.............................F
=================================== FAILURES ===================================
___________________________ test_webhook_valid_ping ____________________________

test_client = <starlette.testclient.TestClient object at 0x7f87407e28b0>

    def test_webhook_valid_ping(test_client: TestClient):
        """Test webhook accepts valid ping event."""
        import hashlib
        import hmac
    
        from stampbot.webhook_handler import webhook_handler
    
        body = json.dumps({"zen": "Design for failure."}).encode()
        signature = (
            "sha256="
            + hmac.new(
                webhook_handler.webhook_secret,
                body,
                hashlib.sha256,
            ).hexdigest()
        )
    
        response = test_client.post(
            "/webhook",
            content=body,
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": signature,
            },
        )
        assert response.status_code == 200
        data = response.json()
>       assert data["status"] == "ok"
E       AssertionError: assert 'ignored' == 'ok'
E         
E         - ok
E         + ignored

tests/test_main.py:177: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:45:39.638435Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-03-27T13:45:39.639533Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 200 OK"
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'ping', 'action': ''}, 'event': 'Received ping event', 'client_ip': 'testclient', 'level': 'info', 'timestamp': '2026-03-27T13:45:39.638435Z'}
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 200 OK"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_valid_ping - AssertionError: assert 'ignored' == 'ok'
  
  - ok
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 101 passed, 3 deselected in 0.85s
operator: core/ReplaceComparisonOperator_Eq_NotEq, occurrence: 2
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -113,7 +113,7 @@
                 result = await self._handle_pull_request(payload)
             elif event_type == "pull_request_review_comment":
                 result = await self._handle_pr_comment(payload)
-            elif event_type == "issue_comment":
+            elif event_type != "issue_comment":
                 # Issue comments can be on PRs too
                 if "pull_request" in payload.get("issue", {}):
                     result = await self._handle_pr_comment(payload)
........................................................................ [ 34%]
.............................F
=================================== FAILURES ===================================
___________________________ test_webhook_valid_ping ____________________________

test_client = <starlette.testclient.TestClient object at 0x7eff053de8b0>

    def test_webhook_valid_ping(test_client: TestClient):
        """Test webhook accepts valid ping event."""
        import hashlib
        import hmac
    
        from stampbot.webhook_handler import webhook_handler
    
        body = json.dumps({"zen": "Design for failure."}).encode()
        signature = (
            "sha256="
            + hmac.new(
                webhook_handler.webhook_secret,
                body,
                hashlib.sha256,
            ).hexdigest()
        )
    
        response = test_client.post(
            "/webhook",
            content=body,
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": signature,
            },
        )
        assert response.status_code == 200
        data = response.json()
>       assert data["status"] == "ok"
E       AssertionError: assert 'ignored' == 'ok'
E         
E         - ok
E         + ignored

tests/test_main.py:177: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:35:08.645549Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-03-27T13:35:08.646483Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 200 OK"
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'ping', 'action': ''}, 'event': 'Received ping event', 'client_ip': 'testclient', 'level': 'info', 'timestamp': '2026-03-27T13:35:08.645549Z'}
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 200 OK"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_valid_ping - AssertionError: assert 'ignored' == 'ok'
  
  - ok
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 101 passed, 3 deselected in 0.85s
operator: core/ReplaceComparisonOperator_Eq_NotEq, occurrence: 3
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -119,7 +119,7 @@
                     result = await self._handle_pr_comment(payload)
                 else:
                     result = {"status": "ignored", "message": "Not a PR comment"}
-            elif event_type == "ping":
+            elif event_type != "ping":
                 result = {"status": "ok", "message": "pong"}
             else:
                 result = {"status": "ignored", "message": f"Event type {event_type} not handled"}
........................................................................ [ 34%]
.............................F
=================================== FAILURES ===================================
___________________________ test_webhook_valid_ping ____________________________

test_client = <starlette.testclient.TestClient object at 0x7f35cd8fa8b0>

    def test_webhook_valid_ping(test_client: TestClient):
        """Test webhook accepts valid ping event."""
        import hashlib
        import hmac
    
        from stampbot.webhook_handler import webhook_handler
    
        body = json.dumps({"zen": "Design for failure."}).encode()
        signature = (
            "sha256="
            + hmac.new(
                webhook_handler.webhook_secret,
                body,
                hashlib.sha256,
            ).hexdigest()
        )
    
        response = test_client.post(
            "/webhook",
            content=body,
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": signature,
            },
        )
        assert response.status_code == 200
        data = response.json()
>       assert data["status"] == "ok"
E       AssertionError: assert 'ignored' == 'ok'
E         
E         - ok
E         + ignored

tests/test_main.py:177: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:34:01.795195Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-03-27T13:34:01.796085Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 200 OK"
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'ping', 'action': ''}, 'event': 'Received ping event', 'client_ip': 'testclient', 'level': 'info', 'timestamp': '2026-03-27T13:34:01.795195Z'}
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 200 OK"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_valid_ping - AssertionError: assert 'ignored' == 'ok'
  
  - ok
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 101 passed, 3 deselected in 0.84s
operator: core/ReplaceComparisonOperator_Eq_NotEq, occurrence: 4
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -187,7 +187,7 @@
                     },
                 )
 
-                if action == "opened":
+                if action != "opened":
                     await run_in_threadpool(
                         github_client.create_pr_review_comment,
                         installation_id,
........................................................................ [ 34%]
........................................................................ [ 68%]
................................................F
=================================== FAILURES ===================================
____________________ test_invalid_repo_config_posts_review _____________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f872dc87590>
mock_github_client = <MagicMock name='github_client' id='140218566470224'>

    @pytest.mark.asyncio
    async def test_invalid_repo_config_posts_review(webhook_handler, mock_github_client):
        """Test invalid repo config logs and posts a review comment."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
        mock_github_client.get_repo_file.return_value = 'chatops_required_permission = "invalid"'
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
        assert result["status"] == "error"
        assert "invalid" in result["message"].lower()
>       mock_github_client.create_pr_review_comment.assert_called_once()

tests/test_webhook_handler.py:518: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <MagicMock name='github_client.create_pr_review_comment' id='140218566471232'>

    def assert_called_once(self):
        """assert that the mock was called only once.
        """
        if not self.call_count == 1:
            msg = ("Expected '%s' to have been called once. Called %s times.%s"
                   % (self._mock_name or 'mock',
                      self.call_count,
                      self._calls_repr()))
>           raise AssertionError(msg)
E           AssertionError: Expected 'create_pr_review_comment' to have been called once. Called 0 times.

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:964: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:41:19.647273Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T13:41:19.648601Z [warning  ] Invalid stampbot.toml in octocat/hello-world: Invalid chatops_required_permission: invalid. Valid values: none, read, triage, write, maintain, admin _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=warning extra={'repo': 'octocat/hello-world', 'error': 'Invalid chatops_required_permission: invalid. Valid values: none, read, triage, write, maintain, admin'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:41:19.647273Z'}
WARNING  stampbot.webhook_handler:webhook_handler.py:176 {'extra': {'repo': 'octocat/hello-world', 'error': 'Invalid chatops_required_permission: invalid. Valid values: none, read, triage, write, maintain, admin'}, 'event': 'Invalid stampbot.toml in octocat/hello-world: Invalid chatops_required_permission: invalid. Valid values: none, read, triage, write, maintain, admin', 'level': 'warning', 'timestamp': '2026-03-27T13:41:19.648601Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_invalid_repo_config_posts_review - AssertionError: Expected 'create_pr_review_comment' to have been called once. Called 0 times.
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 192 passed, 3 deselected in 1.63s
operator: core/ReplaceComparisonOperator_Eq_NotEq, occurrence: 5
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -206,7 +206,7 @@
                     "message": "Invalid repository configuration",
                 }
 
-            if action == "opened":
+            if action != "opened":
                 for label in repo_config.approval_labels:
                     label_exists = await run_in_threadpool(
                         github_client.repo_has_label,
........................................................................ [ 34%]
........................................................................ [ 68%]
.....................F
=================================== FAILURES ===================================
__________________ test_pr_opened_missing_label_logs_warning ___________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f6120d80830>
mock_github_client = <MagicMock name='github_client' id='140055077416176'>

    @pytest.mark.asyncio
    async def test_pr_opened_missing_label_logs_warning(webhook_handler, mock_github_client):
        """Test missing approval label triggers label check."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
        mock_github_client.repo_has_label.return_value = False
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
        assert result["status"] == "success"
>       assert mock_github_client.repo_has_label.call_count == 2
E       AssertionError: assert 0 == 2
E        +  where 0 = <MagicMock name='github_client.repo_has_label' id='140055077413488'>.call_count
E        +    where <MagicMock name='github_client.repo_has_label' id='140055077413488'> = <MagicMock name='github_client' id='140055077416176'>.repo_has_label

tests/test_webhook_handler.py:101: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:12:43.988566Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T14:12:43.989136Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T14:12:43.989649Z [info     ] PR #42 has approval label: autoapprove _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T14:12:43.988566Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T14:12:43.989136Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:278 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'PR #42 has approval label: autoapprove', 'level': 'info', 'timestamp': '2026-03-27T14:12:43.989649Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_opened_missing_label_logs_warning - AssertionError: assert 0 == 2
 +  where 0 = <MagicMock name='github_client.repo_has_label' id='140055077413488'>.call_count
 +    where <MagicMock name='github_client.repo_has_label' id='140055077413488'> = <MagicMock name='github_client' id='140055077416176'>.repo_has_label
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 165 passed, 3 deselected in 1.08s
operator: core/ReplaceComparisonOperator_Eq_NotEq, occurrence: 6
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -314,7 +314,7 @@
                         }
 
             # Check if we should remove approval when label is removed
-            if repo_config.auto_approve_on_label and action == "unlabeled":
+            if repo_config.auto_approve_on_label and action != "unlabeled":
                 removed_label = payload.get("label", {}).get("name")
                 if removed_label in repo_config.approval_labels:
                     logger.info(
........................................................................ [ 34%]
........................................................................ [ 68%]
.........................F
=================================== FAILURES ===================================
________________________ test_pr_unlabeled_autoapprove _________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f372d5f1550>
mock_github_client = <MagicMock name='github_client' id='139875023215920'>

    @pytest.mark.asyncio
    async def test_pr_unlabeled_autoapprove(webhook_handler, mock_github_client):
        """Test removing autoapprove label dismisses approvals."""
        payload = load_fixture("pr_unlabeled_autoapprove")
        mock_github_client.find_bot_reviews.return_value = [111, 222]  # Mock review IDs
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'ignored' == 'success'
E         
E         - success
E         + ignored

tests/test_webhook_handler.py:152: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:11:48.169812Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-03-27T14:11:48.170347Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'unlabeled'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T14:11:48.169812Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T14:11:48.170347Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_unlabeled_autoapprove - AssertionError: assert 'ignored' == 'success'
  
  - success
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 169 passed, 3 deselected in 1.10s
operator: core/ReplaceComparisonOperator_Eq_NotEq, occurrence: 7
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -572,7 +572,7 @@
 
                 org_repo_full_name = None
                 if (
-                    owner_type == "Organization"
+                    owner_type != "Organization"
                     and owner_login
                     and repo_full_name != f"{owner_login}/.github"
                 ):
........................................................................ [ 34%]
........................................................................ [ 68%]
............................................F
=================================== FAILURES ===================================
_____________________ test_repo_config_uses_default_branch _____________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f764ec8bad0>
mock_github_client = <MagicMock name='github_client' id='140146089375264'>

    @pytest.mark.asyncio
    async def test_repo_config_uses_default_branch(webhook_handler, mock_github_client):
        """Test repo config is loaded from the default branch."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
        payload["repository"]["default_branch"] = "develop"
        payload["pull_request"]["base"]["ref"] = "release"
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
        assert result["status"] == "success"
        args = mock_github_client.get_repo_file.call_args[0]
>       assert args[3] == "develop"
E       AssertionError: assert None == 'develop'

tests/test_webhook_handler.py:434: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:35:29.822951Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T13:35:29.823630Z [info     ] No stampbot.toml found in octocat/hello-world or octocat/.github, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': 'octocat/.github'}
2026-03-27T13:35:29.824441Z [info     ] PR #42 has approval label: autoapprove _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:35:29.822951Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': 'octocat/.github'}, 'event': 'No stampbot.toml found in octocat/hello-world or octocat/.github, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:35:29.823630Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:278 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'PR #42 has approval label: autoapprove', 'level': 'info', 'timestamp': '2026-03-27T13:35:29.824441Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_repo_config_uses_default_branch - AssertionError: assert None == 'develop'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 188 passed, 3 deselected in 1.56s
operator: core/ReplaceComparisonOperator_Eq_Lt, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -109,7 +109,7 @@
             )
 
             # Route to appropriate handler
-            if event_type == "pull_request":
+            if event_type < "pull_request":
                 result = await self._handle_pull_request(payload)
             elif event_type == "pull_request_review_comment":
                 result = await self._handle_pr_comment(payload)
........................................................................ [ 34%]
.............................F
=================================== FAILURES ===================================
___________________________ test_webhook_valid_ping ____________________________

test_client = <starlette.testclient.TestClient object at 0x7f05570ca8b0>

    def test_webhook_valid_ping(test_client: TestClient):
        """Test webhook accepts valid ping event."""
        import hashlib
        import hmac
    
        from stampbot.webhook_handler import webhook_handler
    
        body = json.dumps({"zen": "Design for failure."}).encode()
        signature = (
            "sha256="
            + hmac.new(
                webhook_handler.webhook_secret,
                body,
                hashlib.sha256,
            ).hexdigest()
        )
    
        response = test_client.post(
            "/webhook",
            content=body,
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": signature,
            },
        )
        assert response.status_code == 200
        data = response.json()
>       assert data["status"] == "ok"
E       AssertionError: assert 'error' == 'ok'
E         
E         - ok
E         + error

tests/test_main.py:177: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:30:41.357742Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-03-27T13:30:41.358006Z [warning  ] Missing required fields in PR event _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=warning client_ip=testclient
2026-03-27T13:30:41.358821Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 200 OK"
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'ping', 'action': ''}, 'event': 'Received ping event', 'client_ip': 'testclient', 'level': 'info', 'timestamp': '2026-03-27T13:30:41.357742Z'}
WARNING  stampbot.webhook_handler:webhook_handler.py:161 {'event': 'Missing required fields in PR event', 'client_ip': 'testclient', 'level': 'warning', 'timestamp': '2026-03-27T13:30:41.358006Z'}
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 200 OK"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_valid_ping - AssertionError: assert 'error' == 'ok'
  
  - ok
  + error
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 101 passed, 3 deselected in 0.86s
operator: core/ReplaceComparisonOperator_Eq_Lt, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -111,7 +111,7 @@
             # Route to appropriate handler
             if event_type == "pull_request":
                 result = await self._handle_pull_request(payload)
-            elif event_type == "pull_request_review_comment":
+            elif event_type < "pull_request_review_comment":
                 result = await self._handle_pr_comment(payload)
             elif event_type == "issue_comment":
                 # Issue comments can be on PRs too
........................................................................ [ 34%]
.............................F
=================================== FAILURES ===================================
___________________________ test_webhook_valid_ping ____________________________

test_client = <starlette.testclient.TestClient object at 0x7fa371de28b0>

    def test_webhook_valid_ping(test_client: TestClient):
        """Test webhook accepts valid ping event."""
        import hashlib
        import hmac
    
        from stampbot.webhook_handler import webhook_handler
    
        body = json.dumps({"zen": "Design for failure."}).encode()
        signature = (
            "sha256="
            + hmac.new(
                webhook_handler.webhook_secret,
                body,
                hashlib.sha256,
            ).hexdigest()
        )
    
        response = test_client.post(
            "/webhook",
            content=body,
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": signature,
            },
        )
        assert response.status_code == 200
        data = response.json()
>       assert data["status"] == "ok"
E       AssertionError: assert 'ignored' == 'ok'
E         
E         - ok
E         + ignored

tests/test_main.py:177: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:42:29.359008Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-03-27T13:42:29.359900Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 200 OK"
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'ping', 'action': ''}, 'event': 'Received ping event', 'client_ip': 'testclient', 'level': 'info', 'timestamp': '2026-03-27T13:42:29.359008Z'}
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 200 OK"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_valid_ping - AssertionError: assert 'ignored' == 'ok'
  
  - ok
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 101 passed, 3 deselected in 0.85s
operator: core/ReplaceComparisonOperator_Eq_Lt, occurrence: 2
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -113,7 +113,7 @@
                 result = await self._handle_pull_request(payload)
             elif event_type == "pull_request_review_comment":
                 result = await self._handle_pr_comment(payload)
-            elif event_type == "issue_comment":
+            elif event_type < "issue_comment":
                 # Issue comments can be on PRs too
                 if "pull_request" in payload.get("issue", {}):
                     result = await self._handle_pr_comment(payload)
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f789fd0e620>
mock_github_client = <MagicMock name='github_client' id='140156043324992'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
        result = await webhook_handler.handle_event("issue_comment", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'ignored' == 'success'
E         
E         - success
E         + ignored

tests/test_webhook_handler.py:259: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:04:06.519388Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T14:04:06.519388Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - AssertionError: assert 'ignored' == 'success'
  
  - success
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.13s
operator: core/ReplaceComparisonOperator_Eq_Lt, occurrence: 3
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -119,7 +119,7 @@
                     result = await self._handle_pr_comment(payload)
                 else:
                     result = {"status": "ignored", "message": "Not a PR comment"}
-            elif event_type == "ping":
+            elif event_type < "ping":
                 result = {"status": "ok", "message": "pong"}
             else:
                 result = {"status": "ignored", "message": f"Event type {event_type} not handled"}
........................................................................ [ 34%]
.............................F
=================================== FAILURES ===================================
___________________________ test_webhook_valid_ping ____________________________

test_client = <starlette.testclient.TestClient object at 0x7fc55feba8b0>

    def test_webhook_valid_ping(test_client: TestClient):
        """Test webhook accepts valid ping event."""
        import hashlib
        import hmac
    
        from stampbot.webhook_handler import webhook_handler
    
        body = json.dumps({"zen": "Design for failure."}).encode()
        signature = (
            "sha256="
            + hmac.new(
                webhook_handler.webhook_secret,
                body,
                hashlib.sha256,
            ).hexdigest()
        )
    
        response = test_client.post(
            "/webhook",
            content=body,
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": signature,
            },
        )
        assert response.status_code == 200
        data = response.json()
>       assert data["status"] == "ok"
E       AssertionError: assert 'ignored' == 'ok'
E         
E         - ok
E         + ignored

tests/test_main.py:177: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:12:13.125402Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-03-27T14:12:13.126327Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 200 OK"
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'ping', 'action': ''}, 'event': 'Received ping event', 'client_ip': 'testclient', 'level': 'info', 'timestamp': '2026-03-27T14:12:13.125402Z'}
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 200 OK"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_valid_ping - AssertionError: assert 'ignored' == 'ok'
  
  - ok
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 101 passed, 3 deselected in 0.86s
operator: core/ReplaceComparisonOperator_Eq_Lt, occurrence: 4
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -187,7 +187,7 @@
                     },
                 )
 
-                if action == "opened":
+                if action < "opened":
                     await run_in_threadpool(
                         github_client.create_pr_review_comment,
                         installation_id,
........................................................................ [ 34%]
........................................................................ [ 68%]
................................................F
=================================== FAILURES ===================================
____________________ test_invalid_repo_config_posts_review _____________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f322f187530>
mock_github_client = <MagicMock name='github_client' id='139853515975248'>

    @pytest.mark.asyncio
    async def test_invalid_repo_config_posts_review(webhook_handler, mock_github_client):
        """Test invalid repo config logs and posts a review comment."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
        mock_github_client.get_repo_file.return_value = 'chatops_required_permission = "invalid"'
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
        assert result["status"] == "error"
        assert "invalid" in result["message"].lower()
>       mock_github_client.create_pr_review_comment.assert_called_once()

tests/test_webhook_handler.py:518: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <MagicMock name='github_client.create_pr_review_comment' id='139853515976256'>

    def assert_called_once(self):
        """assert that the mock was called only once.
        """
        if not self.call_count == 1:
            msg = ("Expected '%s' to have been called once. Called %s times.%s"
                   % (self._mock_name or 'mock',
                      self.call_count,
                      self._calls_repr()))
>           raise AssertionError(msg)
E           AssertionError: Expected 'create_pr_review_comment' to have been called once. Called 0 times.

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:964: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:03:47.872728Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T14:03:47.874127Z [warning  ] Invalid stampbot.toml in octocat/hello-world: Invalid chatops_required_permission: invalid. Valid values: none, read, triage, write, maintain, admin _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=warning extra={'repo': 'octocat/hello-world', 'error': 'Invalid chatops_required_permission: invalid. Valid values: none, read, triage, write, maintain, admin'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T14:03:47.872728Z'}
WARNING  stampbot.webhook_handler:webhook_handler.py:176 {'extra': {'repo': 'octocat/hello-world', 'error': 'Invalid chatops_required_permission: invalid. Valid values: none, read, triage, write, maintain, admin'}, 'event': 'Invalid stampbot.toml in octocat/hello-world: Invalid chatops_required_permission: invalid. Valid values: none, read, triage, write, maintain, admin', 'level': 'warning', 'timestamp': '2026-03-27T14:03:47.874127Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_invalid_repo_config_posts_review - AssertionError: Expected 'create_pr_review_comment' to have been called once. Called 0 times.
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 192 passed, 3 deselected in 1.65s
operator: core/ReplaceComparisonOperator_Eq_Lt, occurrence: 5
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -206,7 +206,7 @@
                     "message": "Invalid repository configuration",
                 }
 
-            if action == "opened":
+            if action < "opened":
                 for label in repo_config.approval_labels:
                     label_exists = await run_in_threadpool(
                         github_client.repo_has_label,
........................................................................ [ 34%]
........................................................................ [ 68%]
.....................F
=================================== FAILURES ===================================
__________________ test_pr_opened_missing_label_logs_warning ___________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fdcbda84830>
mock_github_client = <MagicMock name='github_client' id='140585991630064'>

    @pytest.mark.asyncio
    async def test_pr_opened_missing_label_logs_warning(webhook_handler, mock_github_client):
        """Test missing approval label triggers label check."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
        mock_github_client.repo_has_label.return_value = False
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
        assert result["status"] == "success"
>       assert mock_github_client.repo_has_label.call_count == 2
E       AssertionError: assert 0 == 2
E        +  where 0 = <MagicMock name='github_client.repo_has_label' id='140585991627376'>.call_count
E        +    where <MagicMock name='github_client.repo_has_label' id='140585991627376'> = <MagicMock name='github_client' id='140585991630064'>.repo_has_label

tests/test_webhook_handler.py:101: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:49:08.323374Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T13:49:08.323940Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T13:49:08.324465Z [info     ] PR #42 has approval label: autoapprove _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:49:08.323374Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:49:08.323940Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:278 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'PR #42 has approval label: autoapprove', 'level': 'info', 'timestamp': '2026-03-27T13:49:08.324465Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_opened_missing_label_logs_warning - AssertionError: assert 0 == 2
 +  where 0 = <MagicMock name='github_client.repo_has_label' id='140585991627376'>.call_count
 +    where <MagicMock name='github_client.repo_has_label' id='140585991627376'> = <MagicMock name='github_client' id='140585991630064'>.repo_has_label
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 165 passed, 3 deselected in 1.08s
operator: core/ReplaceComparisonOperator_Eq_Lt, occurrence: 6
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -314,7 +314,7 @@
                         }
 
             # Check if we should remove approval when label is removed
-            if repo_config.auto_approve_on_label and action == "unlabeled":
+            if repo_config.auto_approve_on_label and action < "unlabeled":
                 removed_label = payload.get("label", {}).get("name")
                 if removed_label in repo_config.approval_labels:
                     logger.info(
........................................................................ [ 34%]
........................................................................ [ 68%]
.........................F
=================================== FAILURES ===================================
________________________ test_pr_unlabeled_autoapprove _________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fe9f37f5450>
mock_github_client = <MagicMock name='github_client' id='140642851413296'>

    @pytest.mark.asyncio
    async def test_pr_unlabeled_autoapprove(webhook_handler, mock_github_client):
        """Test removing autoapprove label dismisses approvals."""
        payload = load_fixture("pr_unlabeled_autoapprove")
        mock_github_client.find_bot_reviews.return_value = [111, 222]  # Mock review IDs
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'ignored' == 'success'
E         
E         - success
E         + ignored

tests/test_webhook_handler.py:152: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:45:10.053975Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-03-27T13:45:10.054510Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'unlabeled'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:45:10.053975Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:45:10.054510Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_unlabeled_autoapprove - AssertionError: assert 'ignored' == 'success'
  
  - success
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 169 passed, 3 deselected in 1.09s
operator: core/ReplaceComparisonOperator_Eq_Lt, occurrence: 7
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -572,7 +572,7 @@
 
                 org_repo_full_name = None
                 if (
-                    owner_type == "Organization"
+                    owner_type < "Organization"
                     and owner_login
                     and repo_full_name != f"{owner_login}/.github"
                 ):
........................................................................ [ 34%]
........................................................................ [ 68%]
.............................................F
=================================== FAILURES ===================================
_____________________ test_org_github_repo_config_fallback _____________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f02a6393230>
mock_github_client = <MagicMock name='github_client' id='139649356366576'>

    @pytest.mark.asyncio
    async def test_org_github_repo_config_fallback(webhook_handler, mock_github_client):
        """Test org .github stampbot.toml is used when repo config is missing."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
        payload["repository"]["full_name"] = "acme/widgets"
        payload["repository"]["default_branch"] = "main"
        payload["repository"]["owner"] = {"login": "acme", "type": "Organization"}
        payload["pull_request"]["labels"] = [{"name": "org-approve"}]
    
        def get_repo_file_side_effect(_installation_id, repo_full_name, _file_path, _ref):
            if repo_full_name == "acme/widgets":
                return None
            if repo_full_name == "acme/.github":
                return 'approval_labels = ["org-approve"]'
            return None
    
        mock_github_client.get_repo_file.side_effect = get_repo_file_side_effect
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'ignored' == 'success'
E         
E         - success
E         + ignored

tests/test_webhook_handler.py:457: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:00:29.060218Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T14:00:29.060742Z [info     ] No stampbot.toml found in acme/widgets, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'acme/widgets', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T14:00:29.060218Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'acme/widgets', 'org_repo': None}, 'event': 'No stampbot.toml found in acme/widgets, using defaults', 'level': 'info', 'timestamp': '2026-03-27T14:00:29.060742Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_org_github_repo_config_fallback - AssertionError: assert 'ignored' == 'success'
  
  - success
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 189 passed, 3 deselected in 1.57s
operator: core/ReplaceComparisonOperator_Eq_LtE, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -109,7 +109,7 @@
             )
 
             # Route to appropriate handler
-            if event_type == "pull_request":
+            if event_type <= "pull_request":
                 result = await self._handle_pull_request(payload)
             elif event_type == "pull_request_review_comment":
                 result = await self._handle_pr_comment(payload)
........................................................................ [ 34%]
.............................F
=================================== FAILURES ===================================
___________________________ test_webhook_valid_ping ____________________________

test_client = <starlette.testclient.TestClient object at 0x7f52157de8b0>

    def test_webhook_valid_ping(test_client: TestClient):
        """Test webhook accepts valid ping event."""
        import hashlib
        import hmac
    
        from stampbot.webhook_handler import webhook_handler
    
        body = json.dumps({"zen": "Design for failure."}).encode()
        signature = (
            "sha256="
            + hmac.new(
                webhook_handler.webhook_secret,
                body,
                hashlib.sha256,
            ).hexdigest()
        )
    
        response = test_client.post(
            "/webhook",
            content=body,
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": signature,
            },
        )
        assert response.status_code == 200
        data = response.json()
>       assert data["status"] == "ok"
E       AssertionError: assert 'error' == 'ok'
E         
E         - ok
E         + error

tests/test_main.py:177: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:35:02.886719Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-03-27T13:35:02.886983Z [warning  ] Missing required fields in PR event _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=warning client_ip=testclient
2026-03-27T13:35:02.887810Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 200 OK"
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'ping', 'action': ''}, 'event': 'Received ping event', 'client_ip': 'testclient', 'level': 'info', 'timestamp': '2026-03-27T13:35:02.886719Z'}
WARNING  stampbot.webhook_handler:webhook_handler.py:161 {'event': 'Missing required fields in PR event', 'client_ip': 'testclient', 'level': 'warning', 'timestamp': '2026-03-27T13:35:02.886983Z'}
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 200 OK"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_valid_ping - AssertionError: assert 'error' == 'ok'
  
  - ok
  + error
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 101 passed, 3 deselected in 0.85s
operator: core/ReplaceComparisonOperator_Eq_LtE, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -111,7 +111,7 @@
             # Route to appropriate handler
             if event_type == "pull_request":
                 result = await self._handle_pull_request(payload)
-            elif event_type == "pull_request_review_comment":
+            elif event_type <= "pull_request_review_comment":
                 result = await self._handle_pr_comment(payload)
             elif event_type == "issue_comment":
                 # Issue comments can be on PRs too
........................................................................ [ 34%]
.............................F
=================================== FAILURES ===================================
___________________________ test_webhook_valid_ping ____________________________

test_client = <starlette.testclient.TestClient object at 0x7fa4122fa8b0>

    def test_webhook_valid_ping(test_client: TestClient):
        """Test webhook accepts valid ping event."""
        import hashlib
        import hmac
    
        from stampbot.webhook_handler import webhook_handler
    
        body = json.dumps({"zen": "Design for failure."}).encode()
        signature = (
            "sha256="
            + hmac.new(
                webhook_handler.webhook_secret,
                body,
                hashlib.sha256,
            ).hexdigest()
        )
    
        response = test_client.post(
            "/webhook",
            content=body,
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": signature,
            },
        )
        assert response.status_code == 200
        data = response.json()
>       assert data["status"] == "ok"
E       AssertionError: assert 'ignored' == 'ok'
E         
E         - ok
E         + ignored

tests/test_main.py:177: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:47:54.640319Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-03-27T13:47:54.641190Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 200 OK"
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'ping', 'action': ''}, 'event': 'Received ping event', 'client_ip': 'testclient', 'level': 'info', 'timestamp': '2026-03-27T13:47:54.640319Z'}
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 200 OK"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_valid_ping - AssertionError: assert 'ignored' == 'ok'
  
  - ok
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 101 passed, 3 deselected in 0.84s
operator: core/ReplaceComparisonOperator_Eq_LtE, occurrence: 2
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -113,7 +113,7 @@
                 result = await self._handle_pull_request(payload)
             elif event_type == "pull_request_review_comment":
                 result = await self._handle_pr_comment(payload)
-            elif event_type == "issue_comment":
+            elif event_type <= "issue_comment":
                 # Issue comments can be on PRs too
                 if "pull_request" in payload.get("issue", {}):
                     result = await self._handle_pr_comment(payload)
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceComparisonOperator_Eq_LtE, occurrence: 3
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -119,7 +119,7 @@
                     result = await self._handle_pr_comment(payload)
                 else:
                     result = {"status": "ignored", "message": "Not a PR comment"}
-            elif event_type == "ping":
+            elif event_type <= "ping":
                 result = {"status": "ok", "message": "pong"}
             else:
                 result = {"status": "ignored", "message": f"Event type {event_type} not handled"}
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceComparisonOperator_Eq_LtE, occurrence: 4
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -187,7 +187,7 @@
                     },
                 )
 
-                if action == "opened":
+                if action <= "opened":
                     await run_in_threadpool(
                         github_client.create_pr_review_comment,
                         installation_id,
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................................F
=================================== FAILURES ===================================
_______________ test_invalid_repo_config_no_review_on_non_opened _______________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f521feb7770>
mock_github_client = <MagicMock name='github_client' id='139990700439072'>

    @pytest.mark.asyncio
    async def test_invalid_repo_config_no_review_on_non_opened(webhook_handler, mock_github_client):
        """Test invalid config skips review comment for non-opened events."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
        payload["action"] = "labeled"
        mock_github_client.get_repo_file.return_value = 'chatops_required_permission = "invalid"'
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
        assert result["status"] == "error"
>       mock_github_client.create_pr_review_comment.assert_not_called()

tests/test_webhook_handler.py:532: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <MagicMock name='github_client.create_pr_review_comment' id='139990700440080'>

    def assert_not_called(self):
        """assert that the mock was never called.
        """
        if self.call_count != 0:
            msg = ("Expected '%s' to not have been called. Called %s times.%s"
                   % (self._mock_name or 'mock',
                      self.call_count,
                      self._calls_repr()))
>           raise AssertionError(msg)
E           AssertionError: Expected 'create_pr_review_comment' to not have been called. Called 1 times.
E           Calls: [call(12345, 'octocat/hello-world', 42, 'Stampbot configuration error in `stampbot.toml`:\n\nInvalid chatops_required_permission: invalid. Valid values: none, read, triage, write, maintain, admin\n\nPlease fix the file to re-enable automation.')].

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:946: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:37:56.956511Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'labeled'}
2026-03-27T13:37:56.957900Z [warning  ] Invalid stampbot.toml in octocat/hello-world: Invalid chatops_required_permission: invalid. Valid values: none, read, triage, write, maintain, admin _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=warning extra={'repo': 'octocat/hello-world', 'error': 'Invalid chatops_required_permission: invalid. Valid values: none, read, triage, write, maintain, admin'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'labeled'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:37:56.956511Z'}
WARNING  stampbot.webhook_handler:webhook_handler.py:176 {'extra': {'repo': 'octocat/hello-world', 'error': 'Invalid chatops_required_permission: invalid. Valid values: none, read, triage, write, maintain, admin'}, 'event': 'Invalid stampbot.toml in octocat/hello-world: Invalid chatops_required_permission: invalid. Valid values: none, read, triage, write, maintain, admin', 'level': 'warning', 'timestamp': '2026-03-27T13:37:56.957900Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_invalid_repo_config_no_review_on_non_opened - AssertionError: Expected 'create_pr_review_comment' to not have been called. Called 1 times.
Calls: [call(12345, 'octocat/hello-world', 42, 'Stampbot configuration error in `stampbot.toml`:\n\nInvalid chatops_required_permission: invalid. Valid values: none, read, triage, write, maintain, admin\n\nPlease fix the file to re-enable automation.')].
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 193 passed, 3 deselected in 1.65s
operator: core/ReplaceComparisonOperator_Eq_LtE, occurrence: 5
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -206,7 +206,7 @@
                     "message": "Invalid repository configuration",
                 }
 
-            if action == "opened":
+            if action <= "opened":
                 for label in repo_config.approval_labels:
                     label_exists = await run_in_threadpool(
                         github_client.repo_has_label,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceComparisonOperator_Eq_LtE, occurrence: 6
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -314,7 +314,7 @@
                         }
 
             # Check if we should remove approval when label is removed
-            if repo_config.auto_approve_on_label and action == "unlabeled":
+            if repo_config.auto_approve_on_label and action <= "unlabeled":
                 removed_label = payload.get("label", {}).get("name")
                 if removed_label in repo_config.approval_labels:
                     logger.info(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceComparisonOperator_Eq_LtE, occurrence: 7
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -572,7 +572,7 @@
 
                 org_repo_full_name = None
                 if (
-                    owner_type == "Organization"
+                    owner_type <= "Organization"
                     and owner_login
                     and repo_full_name != f"{owner_login}/.github"
                 ):
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceComparisonOperator_Eq_Gt, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -109,7 +109,7 @@
             )
 
             # Route to appropriate handler
-            if event_type == "pull_request":
+            if event_type > "pull_request":
                 result = await self._handle_pull_request(payload)
             elif event_type == "pull_request_review_comment":
                 result = await self._handle_pr_comment(payload)
........................................................................ [ 34%]
........................................................................ [ 68%]
..................F
=================================== FAILURES ===================================
______________________________ test_unknown_event ______________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f51db0f7110>

    @pytest.mark.asyncio
    async def test_unknown_event(webhook_handler):
        """Test handling unknown event type."""
        payload = {}
        result = await webhook_handler.handle_event("unknown", payload)
>       assert result["status"] == "ignored"
E       AssertionError: assert 'error' == 'ignored'
E         
E         - ignored
E         + error

tests/test_webhook_handler.py:55: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:03:33.342292Z [info     ] Received unknown event         _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'unknown', 'action': ''}
2026-03-27T14:03:33.342476Z [warning  ] Missing required fields in PR event _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=warning
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'unknown', 'action': ''}, 'event': 'Received unknown event', 'level': 'info', 'timestamp': '2026-03-27T14:03:33.342292Z'}
WARNING  stampbot.webhook_handler:webhook_handler.py:161 {'event': 'Missing required fields in PR event', 'level': 'warning', 'timestamp': '2026-03-27T14:03:33.342476Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_unknown_event - AssertionError: assert 'error' == 'ignored'
  
  - ignored
  + error
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 162 passed, 3 deselected in 1.07s
operator: core/ReplaceComparisonOperator_Eq_Gt, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -111,7 +111,7 @@
             # Route to appropriate handler
             if event_type == "pull_request":
                 result = await self._handle_pull_request(payload)
-            elif event_type == "pull_request_review_comment":
+            elif event_type > "pull_request_review_comment":
                 result = await self._handle_pr_comment(payload)
             elif event_type == "issue_comment":
                 # Issue comments can be on PRs too
........................................................................ [ 34%]
........................................................................ [ 68%]
.......................................................F
=================================== FAILURES ===================================
____________________ test_pull_request_review_comment_event ____________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7ff1d09ab710>
mock_github_client = <MagicMock name='github_client' id='140676563161440'>

    @pytest.mark.asyncio
    async def test_pull_request_review_comment_event(webhook_handler, mock_github_client):
        """Test handling pull_request_review_comment event type."""
        payload = {
            "action": "created",
            "pull_request": {
                "number": 42,
            },
            "comment": {
                "body": "@stampbot approve",
                "user": {"login": "testuser"},
            },
            "repository": {
                "full_name": "owner/repo",
            },
            "installation": {"id": 12345},
        }
    
        result = await webhook_handler.handle_event("pull_request_review_comment", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'ignored' == 'success'
E         
E         - success
E         + ignored

tests/test_webhook_handler.py:625: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:49:11.004612Z [info     ] Received pull_request_review_comment event _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request_review_comment', 'action': 'created'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request_review_comment', 'action': 'created'}, 'event': 'Received pull_request_review_comment event', 'level': 'info', 'timestamp': '2026-03-27T13:49:11.004612Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pull_request_review_comment_event - AssertionError: assert 'ignored' == 'success'
  
  - success
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 199 passed, 3 deselected in 1.59s
operator: core/ReplaceComparisonOperator_Eq_Gt, occurrence: 2
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -113,7 +113,7 @@
                 result = await self._handle_pull_request(payload)
             elif event_type == "pull_request_review_comment":
                 result = await self._handle_pr_comment(payload)
-            elif event_type == "issue_comment":
+            elif event_type > "issue_comment":
                 # Issue comments can be on PRs too
                 if "pull_request" in payload.get("issue", {}):
                     result = await self._handle_pr_comment(payload)
........................................................................ [ 34%]
.............................F
=================================== FAILURES ===================================
___________________________ test_webhook_valid_ping ____________________________

test_client = <starlette.testclient.TestClient object at 0x7f3a682e28b0>

    def test_webhook_valid_ping(test_client: TestClient):
        """Test webhook accepts valid ping event."""
        import hashlib
        import hmac
    
        from stampbot.webhook_handler import webhook_handler
    
        body = json.dumps({"zen": "Design for failure."}).encode()
        signature = (
            "sha256="
            + hmac.new(
                webhook_handler.webhook_secret,
                body,
                hashlib.sha256,
            ).hexdigest()
        )
    
        response = test_client.post(
            "/webhook",
            content=body,
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": signature,
            },
        )
        assert response.status_code == 200
        data = response.json()
>       assert data["status"] == "ok"
E       AssertionError: assert 'ignored' == 'ok'
E         
E         - ok
E         + ignored

tests/test_main.py:177: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:49:12.949657Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-03-27T13:49:12.950607Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 200 OK"
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'ping', 'action': ''}, 'event': 'Received ping event', 'client_ip': 'testclient', 'level': 'info', 'timestamp': '2026-03-27T13:49:12.949657Z'}
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 200 OK"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_valid_ping - AssertionError: assert 'ignored' == 'ok'
  
  - ok
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 101 passed, 3 deselected in 0.85s
operator: core/ReplaceComparisonOperator_Eq_Gt, occurrence: 3
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -119,7 +119,7 @@
                     result = await self._handle_pr_comment(payload)
                 else:
                     result = {"status": "ignored", "message": "Not a PR comment"}
-            elif event_type == "ping":
+            elif event_type > "ping":
                 result = {"status": "ok", "message": "pong"}
             else:
                 result = {"status": "ignored", "message": f"Event type {event_type} not handled"}
........................................................................ [ 34%]
.............................F
=================================== FAILURES ===================================
___________________________ test_webhook_valid_ping ____________________________

test_client = <starlette.testclient.TestClient object at 0x7f3e95dde8b0>

    def test_webhook_valid_ping(test_client: TestClient):
        """Test webhook accepts valid ping event."""
        import hashlib
        import hmac
    
        from stampbot.webhook_handler import webhook_handler
    
        body = json.dumps({"zen": "Design for failure."}).encode()
        signature = (
            "sha256="
            + hmac.new(
                webhook_handler.webhook_secret,
                body,
                hashlib.sha256,
            ).hexdigest()
        )
    
        response = test_client.post(
            "/webhook",
            content=body,
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": signature,
            },
        )
        assert response.status_code == 200
        data = response.json()
>       assert data["status"] == "ok"
E       AssertionError: assert 'ignored' == 'ok'
E         
E         - ok
E         + ignored

tests/test_main.py:177: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:11:26.740276Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-03-27T14:11:26.741192Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 200 OK"
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'ping', 'action': ''}, 'event': 'Received ping event', 'client_ip': 'testclient', 'level': 'info', 'timestamp': '2026-03-27T14:11:26.740276Z'}
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 200 OK"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_valid_ping - AssertionError: assert 'ignored' == 'ok'
  
  - ok
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 101 passed, 3 deselected in 0.85s
operator: core/ReplaceComparisonOperator_Eq_Gt, occurrence: 4
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -187,7 +187,7 @@
                     },
                 )
 
-                if action == "opened":
+                if action > "opened":
                     await run_in_threadpool(
                         github_client.create_pr_review_comment,
                         installation_id,
........................................................................ [ 34%]
........................................................................ [ 68%]
................................................F
=================================== FAILURES ===================================
____________________ test_invalid_repo_config_posts_review _____________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fc1a518b530>
mock_github_client = <MagicMock name='github_client' id='140469676304976'>

    @pytest.mark.asyncio
    async def test_invalid_repo_config_posts_review(webhook_handler, mock_github_client):
        """Test invalid repo config logs and posts a review comment."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
        mock_github_client.get_repo_file.return_value = 'chatops_required_permission = "invalid"'
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
        assert result["status"] == "error"
        assert "invalid" in result["message"].lower()
>       mock_github_client.create_pr_review_comment.assert_called_once()

tests/test_webhook_handler.py:518: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <MagicMock name='github_client.create_pr_review_comment' id='140469676305984'>

    def assert_called_once(self):
        """assert that the mock was called only once.
        """
        if not self.call_count == 1:
            msg = ("Expected '%s' to have been called once. Called %s times.%s"
                   % (self._mock_name or 'mock',
                      self.call_count,
                      self._calls_repr()))
>           raise AssertionError(msg)
E           AssertionError: Expected 'create_pr_review_comment' to have been called once. Called 0 times.

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:964: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:39:49.429134Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T13:39:49.430535Z [warning  ] Invalid stampbot.toml in octocat/hello-world: Invalid chatops_required_permission: invalid. Valid values: none, read, triage, write, maintain, admin _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=warning extra={'repo': 'octocat/hello-world', 'error': 'Invalid chatops_required_permission: invalid. Valid values: none, read, triage, write, maintain, admin'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:39:49.429134Z'}
WARNING  stampbot.webhook_handler:webhook_handler.py:176 {'extra': {'repo': 'octocat/hello-world', 'error': 'Invalid chatops_required_permission: invalid. Valid values: none, read, triage, write, maintain, admin'}, 'event': 'Invalid stampbot.toml in octocat/hello-world: Invalid chatops_required_permission: invalid. Valid values: none, read, triage, write, maintain, admin', 'level': 'warning', 'timestamp': '2026-03-27T13:39:49.430535Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_invalid_repo_config_posts_review - AssertionError: Expected 'create_pr_review_comment' to have been called once. Called 0 times.
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 192 passed, 3 deselected in 1.63s
operator: core/ReplaceComparisonOperator_Eq_Gt, occurrence: 5
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -206,7 +206,7 @@
                     "message": "Invalid repository configuration",
                 }
 
-            if action == "opened":
+            if action > "opened":
                 for label in repo_config.approval_labels:
                     label_exists = await run_in_threadpool(
                         github_client.repo_has_label,
........................................................................ [ 34%]
........................................................................ [ 68%]
.....................F
=================================== FAILURES ===================================
__________________ test_pr_opened_missing_label_logs_warning ___________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7facc5c80830>
mock_github_client = <MagicMock name='github_client' id='140379969400048'>

    @pytest.mark.asyncio
    async def test_pr_opened_missing_label_logs_warning(webhook_handler, mock_github_client):
        """Test missing approval label triggers label check."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
        mock_github_client.repo_has_label.return_value = False
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
        assert result["status"] == "success"
>       assert mock_github_client.repo_has_label.call_count == 2
E       AssertionError: assert 0 == 2
E        +  where 0 = <MagicMock name='github_client.repo_has_label' id='140379969397360'>.call_count
E        +    where <MagicMock name='github_client.repo_has_label' id='140379969397360'> = <MagicMock name='github_client' id='140379969400048'>.repo_has_label

tests/test_webhook_handler.py:101: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:05:08.173664Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T14:05:08.174223Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T14:05:08.174731Z [info     ] PR #42 has approval label: autoapprove _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T14:05:08.173664Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T14:05:08.174223Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:278 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'PR #42 has approval label: autoapprove', 'level': 'info', 'timestamp': '2026-03-27T14:05:08.174731Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_opened_missing_label_logs_warning - AssertionError: assert 0 == 2
 +  where 0 = <MagicMock name='github_client.repo_has_label' id='140379969397360'>.call_count
 +    where <MagicMock name='github_client.repo_has_label' id='140379969397360'> = <MagicMock name='github_client' id='140379969400048'>.repo_has_label
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 165 passed, 3 deselected in 1.10s
operator: core/ReplaceComparisonOperator_Eq_Gt, occurrence: 6
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -314,7 +314,7 @@
                         }
 
             # Check if we should remove approval when label is removed
-            if repo_config.auto_approve_on_label and action == "unlabeled":
+            if repo_config.auto_approve_on_label and action > "unlabeled":
                 removed_label = payload.get("label", {}).get("name")
                 if removed_label in repo_config.approval_labels:
                     logger.info(
........................................................................ [ 34%]
........................................................................ [ 68%]
.........................F
=================================== FAILURES ===================================
________________________ test_pr_unlabeled_autoapprove _________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f6b032e9350>
mock_github_client = <MagicMock name='github_client' id='140097656577328'>

    @pytest.mark.asyncio
    async def test_pr_unlabeled_autoapprove(webhook_handler, mock_github_client):
        """Test removing autoapprove label dismisses approvals."""
        payload = load_fixture("pr_unlabeled_autoapprove")
        mock_github_client.find_bot_reviews.return_value = [111, 222]  # Mock review IDs
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'ignored' == 'success'
E         
E         - success
E         + ignored

tests/test_webhook_handler.py:152: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:07:04.907933Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-03-27T14:07:04.908467Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'unlabeled'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T14:07:04.907933Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T14:07:04.908467Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_unlabeled_autoapprove - AssertionError: assert 'ignored' == 'success'
  
  - success
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 169 passed, 3 deselected in 1.10s
operator: core/ReplaceComparisonOperator_Eq_Gt, occurrence: 7
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -572,7 +572,7 @@
 
                 org_repo_full_name = None
                 if (
-                    owner_type == "Organization"
+                    owner_type > "Organization"
                     and owner_login
                     and repo_full_name != f"{owner_login}/.github"
                 ):
........................................................................ [ 34%]
........................................................................ [ 68%]
............................................F
=================================== FAILURES ===================================
_____________________ test_repo_config_uses_default_branch _____________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f0b9e8a3b30>
mock_github_client = <MagicMock name='github_client' id='139687880390176'>

    @pytest.mark.asyncio
    async def test_repo_config_uses_default_branch(webhook_handler, mock_github_client):
        """Test repo config is loaded from the default branch."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
        payload["repository"]["default_branch"] = "develop"
        payload["pull_request"]["base"]["ref"] = "release"
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
        assert result["status"] == "success"
        args = mock_github_client.get_repo_file.call_args[0]
>       assert args[3] == "develop"
E       AssertionError: assert None == 'develop'

tests/test_webhook_handler.py:434: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:07:40.966248Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T14:07:40.966967Z [info     ] No stampbot.toml found in octocat/hello-world or octocat/.github, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': 'octocat/.github'}
2026-03-27T14:07:40.967747Z [info     ] PR #42 has approval label: autoapprove _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T14:07:40.966248Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': 'octocat/.github'}, 'event': 'No stampbot.toml found in octocat/hello-world or octocat/.github, using defaults', 'level': 'info', 'timestamp': '2026-03-27T14:07:40.966967Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:278 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'PR #42 has approval label: autoapprove', 'level': 'info', 'timestamp': '2026-03-27T14:07:40.967747Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_repo_config_uses_default_branch - AssertionError: assert None == 'develop'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 188 passed, 3 deselected in 1.58s
operator: core/ReplaceComparisonOperator_Eq_GtE, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -109,7 +109,7 @@
             )
 
             # Route to appropriate handler
-            if event_type == "pull_request":
+            if event_type >= "pull_request":
                 result = await self._handle_pull_request(payload)
             elif event_type == "pull_request_review_comment":
                 result = await self._handle_pr_comment(payload)
........................................................................ [ 34%]
........................................................................ [ 68%]
..................F
=================================== FAILURES ===================================
______________________________ test_unknown_event ______________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fb1e2dfb110>

    @pytest.mark.asyncio
    async def test_unknown_event(webhook_handler):
        """Test handling unknown event type."""
        payload = {}
        result = await webhook_handler.handle_event("unknown", payload)
>       assert result["status"] == "ignored"
E       AssertionError: assert 'error' == 'ignored'
E         
E         - ignored
E         + error

tests/test_webhook_handler.py:55: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:31:19.780758Z [info     ] Received unknown event         _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'unknown', 'action': ''}
2026-03-27T13:31:19.780981Z [warning  ] Missing required fields in PR event _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=warning
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'unknown', 'action': ''}, 'event': 'Received unknown event', 'level': 'info', 'timestamp': '2026-03-27T13:31:19.780758Z'}
WARNING  stampbot.webhook_handler:webhook_handler.py:161 {'event': 'Missing required fields in PR event', 'level': 'warning', 'timestamp': '2026-03-27T13:31:19.780981Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_unknown_event - AssertionError: assert 'error' == 'ignored'
  
  - ignored
  + error
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 162 passed, 3 deselected in 1.06s
operator: core/ReplaceComparisonOperator_Eq_GtE, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -111,7 +111,7 @@
             # Route to appropriate handler
             if event_type == "pull_request":
                 result = await self._handle_pull_request(payload)
-            elif event_type == "pull_request_review_comment":
+            elif event_type >= "pull_request_review_comment":
                 result = await self._handle_pr_comment(payload)
             elif event_type == "issue_comment":
                 # Issue comments can be on PRs too
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceComparisonOperator_Eq_GtE, occurrence: 2
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -113,7 +113,7 @@
                 result = await self._handle_pull_request(payload)
             elif event_type == "pull_request_review_comment":
                 result = await self._handle_pr_comment(payload)
-            elif event_type == "issue_comment":
+            elif event_type >= "issue_comment":
                 # Issue comments can be on PRs too
                 if "pull_request" in payload.get("issue", {}):
                     result = await self._handle_pr_comment(payload)
........................................................................ [ 34%]
.............................F
=================================== FAILURES ===================================
___________________________ test_webhook_valid_ping ____________________________

test_client = <starlette.testclient.TestClient object at 0x7fde3e9c68b0>

    def test_webhook_valid_ping(test_client: TestClient):
        """Test webhook accepts valid ping event."""
        import hashlib
        import hmac
    
        from stampbot.webhook_handler import webhook_handler
    
        body = json.dumps({"zen": "Design for failure."}).encode()
        signature = (
            "sha256="
            + hmac.new(
                webhook_handler.webhook_secret,
                body,
                hashlib.sha256,
            ).hexdigest()
        )
    
        response = test_client.post(
            "/webhook",
            content=body,
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": signature,
            },
        )
        assert response.status_code == 200
        data = response.json()
>       assert data["status"] == "ok"
E       AssertionError: assert 'ignored' == 'ok'
E         
E         - ok
E         + ignored

tests/test_main.py:177: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:04:40.449646Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-03-27T14:04:40.450541Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 200 OK"
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'ping', 'action': ''}, 'event': 'Received ping event', 'client_ip': 'testclient', 'level': 'info', 'timestamp': '2026-03-27T14:04:40.449646Z'}
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 200 OK"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_valid_ping - AssertionError: assert 'ignored' == 'ok'
  
  - ok
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 101 passed, 3 deselected in 0.86s
operator: core/ReplaceComparisonOperator_Eq_GtE, occurrence: 3
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -119,7 +119,7 @@
                     result = await self._handle_pr_comment(payload)
                 else:
                     result = {"status": "ignored", "message": "Not a PR comment"}
-            elif event_type == "ping":
+            elif event_type >= "ping":
                 result = {"status": "ok", "message": "pong"}
             else:
                 result = {"status": "ignored", "message": f"Event type {event_type} not handled"}
........................................................................ [ 34%]
........................................................................ [ 68%]
..................F
=================================== FAILURES ===================================
______________________________ test_unknown_event ______________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f88ad213110>

    @pytest.mark.asyncio
    async def test_unknown_event(webhook_handler):
        """Test handling unknown event type."""
        payload = {}
        result = await webhook_handler.handle_event("unknown", payload)
>       assert result["status"] == "ignored"
E       AssertionError: assert 'ok' == 'ignored'
E         
E         - ignored
E         + ok

tests/test_webhook_handler.py:55: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:30:11.446341Z [info     ] Received unknown event         _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'unknown', 'action': ''}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'unknown', 'action': ''}, 'event': 'Received unknown event', 'level': 'info', 'timestamp': '2026-03-27T13:30:11.446341Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_unknown_event - AssertionError: assert 'ok' == 'ignored'
  
  - ignored
  + ok
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 162 passed, 3 deselected in 1.07s
operator: core/ReplaceComparisonOperator_Eq_GtE, occurrence: 4
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -187,7 +187,7 @@
                     },
                 )
 
-                if action == "opened":
+                if action >= "opened":
                     await run_in_threadpool(
                         github_client.create_pr_review_comment,
                         installation_id,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceComparisonOperator_Eq_GtE, occurrence: 5
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -206,7 +206,7 @@
                     "message": "Invalid repository configuration",
                 }
 
-            if action == "opened":
+            if action >= "opened":
                 for label in repo_config.approval_labels:
                     label_exists = await run_in_threadpool(
                         github_client.repo_has_label,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceComparisonOperator_Eq_GtE, occurrence: 6
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -314,7 +314,7 @@
                         }
 
             # Check if we should remove approval when label is removed
-            if repo_config.auto_approve_on_label and action == "unlabeled":
+            if repo_config.auto_approve_on_label and action >= "unlabeled":
                 removed_label = payload.get("label", {}).get("name")
                 if removed_label in repo_config.approval_labels:
                     logger.info(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/ReplaceComparisonOperator_Eq_GtE, occurrence: 7
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -572,7 +572,7 @@
 
                 org_repo_full_name = None
                 if (
-                    owner_type == "Organization"
+                    owner_type >= "Organization"
                     and owner_login
                     and repo_full_name != f"{owner_login}/.github"
                 ):
........................................................................ [ 34%]
........................................................................ [ 68%]
............................................F
=================================== FAILURES ===================================
_____________________ test_repo_config_uses_default_branch _____________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f5765f9f590>
mock_github_client = <MagicMock name='github_client' id='140013348566560'>

    @pytest.mark.asyncio
    async def test_repo_config_uses_default_branch(webhook_handler, mock_github_client):
        """Test repo config is loaded from the default branch."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
        payload["repository"]["default_branch"] = "develop"
        payload["pull_request"]["base"]["ref"] = "release"
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
        assert result["status"] == "success"
        args = mock_github_client.get_repo_file.call_args[0]
>       assert args[3] == "develop"
E       AssertionError: assert None == 'develop'

tests/test_webhook_handler.py:434: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:08:07.952287Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T14:08:07.952986Z [info     ] No stampbot.toml found in octocat/hello-world or octocat/.github, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': 'octocat/.github'}
2026-03-27T14:08:07.953764Z [info     ] PR #42 has approval label: autoapprove _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T14:08:07.952287Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': 'octocat/.github'}, 'event': 'No stampbot.toml found in octocat/hello-world or octocat/.github, using defaults', 'level': 'info', 'timestamp': '2026-03-27T14:08:07.952986Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:278 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'PR #42 has approval label: autoapprove', 'level': 'info', 'timestamp': '2026-03-27T14:08:07.953764Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_repo_config_uses_default_branch - AssertionError: assert None == 'develop'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 188 passed, 3 deselected in 1.57s
operator: core/ReplaceComparisonOperator_Eq_Is, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -109,7 +109,7 @@
             )
 
             # Route to appropriate handler
-            if event_type == "pull_request":
+            if event_type is "pull_request":
                 result = await self._handle_pull_request(payload)
             elif event_type == "pull_request_review_comment":
                 result = await self._handle_pr_comment(payload)
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
=============================== warnings summary ===============================
tests/test_main.py::test_lifespan_startup_shutdown_configured
  /home/runner/work/stampbot/stampbot/stampbot/webhook_handler.py:112: SyntaxWarning: "is" with 'str' literal. Did you mean "=="?
    if event_type is "pull_request":

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
211 passed, 3 deselected, 1 warning in 1.61s
operator: core/ReplaceComparisonOperator_Eq_Is, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -111,7 +111,7 @@
             # Route to appropriate handler
             if event_type == "pull_request":
                 result = await self._handle_pull_request(payload)
-            elif event_type == "pull_request_review_comment":
+            elif event_type is "pull_request_review_comment":
                 result = await self._handle_pr_comment(payload)
             elif event_type == "issue_comment":
                 # Issue comments can be on PRs too
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
=============================== warnings summary ===============================
tests/test_main.py::test_lifespan_startup_shutdown_configured
  /home/runner/work/stampbot/stampbot/stampbot/webhook_handler.py:114: SyntaxWarning: "is" with 'str' literal. Did you mean "=="?
    elif event_type is "pull_request_review_comment":

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
211 passed, 3 deselected, 1 warning in 1.62s
operator: core/ReplaceComparisonOperator_Eq_Is, occurrence: 2
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -113,7 +113,7 @@
                 result = await self._handle_pull_request(payload)
             elif event_type == "pull_request_review_comment":
                 result = await self._handle_pr_comment(payload)
-            elif event_type == "issue_comment":
+            elif event_type is "issue_comment":
                 # Issue comments can be on PRs too
                 if "pull_request" in payload.get("issue", {}):
                     result = await self._handle_pr_comment(payload)
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
=============================== warnings summary ===============================
tests/test_main.py::test_lifespan_startup_shutdown_configured
  /home/runner/work/stampbot/stampbot/stampbot/webhook_handler.py:116: SyntaxWarning: "is" with 'str' literal. Did you mean "=="?
    elif event_type is "issue_comment":

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
211 passed, 3 deselected, 1 warning in 1.62s
operator: core/ReplaceComparisonOperator_Eq_Is, occurrence: 3
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -119,7 +119,7 @@
                     result = await self._handle_pr_comment(payload)
                 else:
                     result = {"status": "ignored", "message": "Not a PR comment"}
-            elif event_type == "ping":
+            elif event_type is "ping":
                 result = {"status": "ok", "message": "pong"}
             else:
                 result = {"status": "ignored", "message": f"Event type {event_type} not handled"}
........................................................................ [ 34%]
.............................F
=================================== FAILURES ===================================
___________________________ test_webhook_valid_ping ____________________________

test_client = <starlette.testclient.TestClient object at 0x7fe4d17ee8b0>

    def test_webhook_valid_ping(test_client: TestClient):
        """Test webhook accepts valid ping event."""
        import hashlib
        import hmac
    
        from stampbot.webhook_handler import webhook_handler
    
        body = json.dumps({"zen": "Design for failure."}).encode()
        signature = (
            "sha256="
            + hmac.new(
                webhook_handler.webhook_secret,
                body,
                hashlib.sha256,
            ).hexdigest()
        )
    
        response = test_client.post(
            "/webhook",
            content=body,
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": signature,
            },
        )
        assert response.status_code == 200
        data = response.json()
>       assert data["status"] == "ok"
E       AssertionError: assert 'ignored' == 'ok'
E         
E         - ok
E         + ignored

tests/test_main.py:177: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:41:12.941664Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-03-27T13:41:12.942551Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 200 OK"
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'ping', 'action': ''}, 'event': 'Received ping event', 'client_ip': 'testclient', 'level': 'info', 'timestamp': '2026-03-27T13:41:12.941664Z'}
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 200 OK"
=============================== warnings summary ===============================
tests/test_main.py::test_lifespan_startup_shutdown_configured
  /home/runner/work/stampbot/stampbot/stampbot/webhook_handler.py:122: SyntaxWarning: "is" with 'str' literal. Did you mean "=="?
    elif event_type is "ping":

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_valid_ping - AssertionError: assert 'ignored' == 'ok'
  
  - ok
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 101 passed, 3 deselected, 1 warning in 0.85s
operator: core/ReplaceComparisonOperator_Eq_Is, occurrence: 4
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -187,7 +187,7 @@
                     },
                 )
 
-                if action == "opened":
+                if action is "opened":
                     await run_in_threadpool(
                         github_client.create_pr_review_comment,
                         installation_id,
........................................................................ [ 34%]
........................................................................ [ 68%]
................................................F
=================================== FAILURES ===================================
____________________ test_invalid_repo_config_posts_review _____________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f3ef9fc1070>
mock_github_client = <MagicMock name='github_client' id='139908457475312'>

    @pytest.mark.asyncio
    async def test_invalid_repo_config_posts_review(webhook_handler, mock_github_client):
        """Test invalid repo config logs and posts a review comment."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
        mock_github_client.get_repo_file.return_value = 'chatops_required_permission = "invalid"'
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
        assert result["status"] == "error"
        assert "invalid" in result["message"].lower()
>       mock_github_client.create_pr_review_comment.assert_called_once()

tests/test_webhook_handler.py:518: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <MagicMock name='github_client.create_pr_review_comment' id='139908457476320'>

    def assert_called_once(self):
        """assert that the mock was called only once.
        """
        if not self.call_count == 1:
            msg = ("Expected '%s' to have been called once. Called %s times.%s"
                   % (self._mock_name or 'mock',
                      self.call_count,
                      self._calls_repr()))
>           raise AssertionError(msg)
E           AssertionError: Expected 'create_pr_review_comment' to have been called once. Called 0 times.

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:964: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:47:23.700356Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T13:47:23.701845Z [warning  ] Invalid stampbot.toml in octocat/hello-world: Invalid chatops_required_permission: invalid. Valid values: none, read, triage, write, maintain, admin _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=warning extra={'repo': 'octocat/hello-world', 'error': 'Invalid chatops_required_permission: invalid. Valid values: none, read, triage, write, maintain, admin'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:47:23.700356Z'}
WARNING  stampbot.webhook_handler:webhook_handler.py:176 {'extra': {'repo': 'octocat/hello-world', 'error': 'Invalid chatops_required_permission: invalid. Valid values: none, read, triage, write, maintain, admin'}, 'event': 'Invalid stampbot.toml in octocat/hello-world: Invalid chatops_required_permission: invalid. Valid values: none, read, triage, write, maintain, admin', 'level': 'warning', 'timestamp': '2026-03-27T13:47:23.701845Z'}
=============================== warnings summary ===============================
tests/test_main.py::test_lifespan_startup_shutdown_configured
  /home/runner/work/stampbot/stampbot/stampbot/webhook_handler.py:190: SyntaxWarning: "is" with 'str' literal. Did you mean "=="?
    if action is "opened":

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_invalid_repo_config_posts_review - AssertionError: Expected 'create_pr_review_comment' to have been called once. Called 0 times.
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 192 passed, 3 deselected, 1 warning in 1.65s
operator: core/ReplaceComparisonOperator_Eq_Is, occurrence: 5
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -206,7 +206,7 @@
                     "message": "Invalid repository configuration",
                 }
 
-            if action == "opened":
+            if action is "opened":
                 for label in repo_config.approval_labels:
                     label_exists = await run_in_threadpool(
                         github_client.repo_has_label,
........................................................................ [ 34%]
........................................................................ [ 68%]
.....................F
=================================== FAILURES ===================================
__________________ test_pr_opened_missing_label_logs_warning ___________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fe8217a0830>
mock_github_client = <MagicMock name='github_client' id='140634910730496'>

    @pytest.mark.asyncio
    async def test_pr_opened_missing_label_logs_warning(webhook_handler, mock_github_client):
        """Test missing approval label triggers label check."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
        mock_github_client.repo_has_label.return_value = False
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
        assert result["status"] == "success"
>       assert mock_github_client.repo_has_label.call_count == 2
E       AssertionError: assert 0 == 2
E        +  where 0 = <MagicMock name='github_client.repo_has_label' id='140634910729152'>.call_count
E        +    where <MagicMock name='github_client.repo_has_label' id='140634910729152'> = <MagicMock name='github_client' id='140634910730496'>.repo_has_label

tests/test_webhook_handler.py:101: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:56:25.255849Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T13:56:25.256382Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T13:56:25.256912Z [info     ] PR #42 has approval label: autoapprove _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:56:25.255849Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:56:25.256382Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:278 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'PR #42 has approval label: autoapprove', 'level': 'info', 'timestamp': '2026-03-27T13:56:25.256912Z'}
=============================== warnings summary ===============================
tests/test_main.py::test_lifespan_startup_shutdown_configured
  /home/runner/work/stampbot/stampbot/stampbot/webhook_handler.py:209: SyntaxWarning: "is" with 'str' literal. Did you mean "=="?
    if action is "opened":

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_opened_missing_label_logs_warning - AssertionError: assert 0 == 2
 +  where 0 = <MagicMock name='github_client.repo_has_label' id='140634910729152'>.call_count
 +    where <MagicMock name='github_client.repo_has_label' id='140634910729152'> = <MagicMock name='github_client' id='140634910730496'>.repo_has_label
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 165 passed, 3 deselected, 1 warning in 1.08s
operator: core/ReplaceComparisonOperator_Eq_Is, occurrence: 6
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -314,7 +314,7 @@
                         }
 
             # Check if we should remove approval when label is removed
-            if repo_config.auto_approve_on_label and action == "unlabeled":
+            if repo_config.auto_approve_on_label and action is "unlabeled":
                 removed_label = payload.get("label", {}).get("name")
                 if removed_label in repo_config.approval_labels:
                     logger.info(
........................................................................ [ 34%]
........................................................................ [ 68%]
.........................F
=================================== FAILURES ===================================
________________________ test_pr_unlabeled_autoapprove _________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fbf81ff5650>
mock_github_client = <MagicMock name='github_client' id='140460558589904'>

    @pytest.mark.asyncio
    async def test_pr_unlabeled_autoapprove(webhook_handler, mock_github_client):
        """Test removing autoapprove label dismisses approvals."""
        payload = load_fixture("pr_unlabeled_autoapprove")
        mock_github_client.find_bot_reviews.return_value = [111, 222]  # Mock review IDs
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'ignored' == 'success'
E         
E         - success
E         + ignored

tests/test_webhook_handler.py:152: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:36:06.100500Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-03-27T13:36:06.101083Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'unlabeled'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:36:06.100500Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:36:06.101083Z'}
=============================== warnings summary ===============================
tests/test_main.py::test_lifespan_startup_shutdown_configured
  /home/runner/work/stampbot/stampbot/stampbot/webhook_handler.py:317: SyntaxWarning: "is" with 'str' literal. Did you mean "=="?
    if repo_config.auto_approve_on_label and action is "unlabeled":

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_unlabeled_autoapprove - AssertionError: assert 'ignored' == 'success'
  
  - success
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 169 passed, 3 deselected, 1 warning in 1.09s
operator: core/ReplaceComparisonOperator_Eq_Is, occurrence: 7
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -572,7 +572,7 @@
 
                 org_repo_full_name = None
                 if (
-                    owner_type == "Organization"
+                    owner_type is "Organization"
                     and owner_login
                     and repo_full_name != f"{owner_login}/.github"
                 ):
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
=============================== warnings summary ===============================
tests/test_main.py::test_lifespan_startup_shutdown_configured
  /home/runner/work/stampbot/stampbot/stampbot/webhook_handler.py:575: SyntaxWarning: "is" with 'str' literal. Did you mean "=="?
    owner_type is "Organization"

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
211 passed, 3 deselected, 1 warning in 1.63s
operator: core/ReplaceComparisonOperator_Eq_IsNot, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -109,7 +109,7 @@
             )
 
             # Route to appropriate handler
-            if event_type == "pull_request":
+            if event_type is not "pull_request":
                 result = await self._handle_pull_request(payload)
             elif event_type == "pull_request_review_comment":
                 result = await self._handle_pr_comment(payload)
........................................................................ [ 34%]
.............................F
=================================== FAILURES ===================================
___________________________ test_webhook_valid_ping ____________________________

test_client = <starlette.testclient.TestClient object at 0x7fcbe92068b0>

    def test_webhook_valid_ping(test_client: TestClient):
        """Test webhook accepts valid ping event."""
        import hashlib
        import hmac
    
        from stampbot.webhook_handler import webhook_handler
    
        body = json.dumps({"zen": "Design for failure."}).encode()
        signature = (
            "sha256="
            + hmac.new(
                webhook_handler.webhook_secret,
                body,
                hashlib.sha256,
            ).hexdigest()
        )
    
        response = test_client.post(
            "/webhook",
            content=body,
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": signature,
            },
        )
        assert response.status_code == 200
        data = response.json()
>       assert data["status"] == "ok"
E       AssertionError: assert 'error' == 'ok'
E         
E         - ok
E         + error

tests/test_main.py:177: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:28:06.424556Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-03-27T13:28:06.424805Z [warning  ] Missing required fields in PR event _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=warning client_ip=testclient
2026-03-27T13:28:06.425702Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 200 OK"
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'ping', 'action': ''}, 'event': 'Received ping event', 'client_ip': 'testclient', 'level': 'info', 'timestamp': '2026-03-27T13:28:06.424556Z'}
WARNING  stampbot.webhook_handler:webhook_handler.py:161 {'event': 'Missing required fields in PR event', 'client_ip': 'testclient', 'level': 'warning', 'timestamp': '2026-03-27T13:28:06.424805Z'}
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 200 OK"
=============================== warnings summary ===============================
tests/test_main.py::test_lifespan_startup_shutdown_configured
  /home/runner/work/stampbot/stampbot/stampbot/webhook_handler.py:112: SyntaxWarning: "is not" with 'str' literal. Did you mean "!="?
    if event_type is not "pull_request":

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_valid_ping - AssertionError: assert 'error' == 'ok'
  
  - ok
  + error
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 101 passed, 3 deselected, 1 warning in 0.86s
operator: core/ReplaceComparisonOperator_Eq_IsNot, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -111,7 +111,7 @@
             # Route to appropriate handler
             if event_type == "pull_request":
                 result = await self._handle_pull_request(payload)
-            elif event_type == "pull_request_review_comment":
+            elif event_type is not "pull_request_review_comment":
                 result = await self._handle_pr_comment(payload)
             elif event_type == "issue_comment":
                 # Issue comments can be on PRs too
........................................................................ [ 34%]
.............................F
=================================== FAILURES ===================================
___________________________ test_webhook_valid_ping ____________________________

test_client = <starlette.testclient.TestClient object at 0x7f8a553068b0>

    def test_webhook_valid_ping(test_client: TestClient):
        """Test webhook accepts valid ping event."""
        import hashlib
        import hmac
    
        from stampbot.webhook_handler import webhook_handler
    
        body = json.dumps({"zen": "Design for failure."}).encode()
        signature = (
            "sha256="
            + hmac.new(
                webhook_handler.webhook_secret,
                body,
                hashlib.sha256,
            ).hexdigest()
        )
    
        response = test_client.post(
            "/webhook",
            content=body,
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": signature,
            },
        )
        assert response.status_code == 200
        data = response.json()
>       assert data["status"] == "ok"
E       AssertionError: assert 'ignored' == 'ok'
E         
E         - ok
E         + ignored

tests/test_main.py:177: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:09:36.665000Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-03-27T14:09:36.665922Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 200 OK"
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'ping', 'action': ''}, 'event': 'Received ping event', 'client_ip': 'testclient', 'level': 'info', 'timestamp': '2026-03-27T14:09:36.665000Z'}
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 200 OK"
=============================== warnings summary ===============================
tests/test_main.py::test_lifespan_startup_shutdown_configured
  /home/runner/work/stampbot/stampbot/stampbot/webhook_handler.py:114: SyntaxWarning: "is not" with 'str' literal. Did you mean "!="?
    elif event_type is not "pull_request_review_comment":

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_valid_ping - AssertionError: assert 'ignored' == 'ok'
  
  - ok
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 101 passed, 3 deselected, 1 warning in 0.87s
operator: core/ReplaceComparisonOperator_Eq_IsNot, occurrence: 2
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -113,7 +113,7 @@
                 result = await self._handle_pull_request(payload)
             elif event_type == "pull_request_review_comment":
                 result = await self._handle_pr_comment(payload)
-            elif event_type == "issue_comment":
+            elif event_type is not "issue_comment":
                 # Issue comments can be on PRs too
                 if "pull_request" in payload.get("issue", {}):
                     result = await self._handle_pr_comment(payload)
........................................................................ [ 34%]
.............................F
=================================== FAILURES ===================================
___________________________ test_webhook_valid_ping ____________________________

test_client = <starlette.testclient.TestClient object at 0x7f7600fee8b0>

    def test_webhook_valid_ping(test_client: TestClient):
        """Test webhook accepts valid ping event."""
        import hashlib
        import hmac
    
        from stampbot.webhook_handler import webhook_handler
    
        body = json.dumps({"zen": "Design for failure."}).encode()
        signature = (
            "sha256="
            + hmac.new(
                webhook_handler.webhook_secret,
                body,
                hashlib.sha256,
            ).hexdigest()
        )
    
        response = test_client.post(
            "/webhook",
            content=body,
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": signature,
            },
        )
        assert response.status_code == 200
        data = response.json()
>       assert data["status"] == "ok"
E       AssertionError: assert 'ignored' == 'ok'
E         
E         - ok
E         + ignored

tests/test_main.py:177: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:41:16.987267Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-03-27T13:41:16.988400Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 200 OK"
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'ping', 'action': ''}, 'event': 'Received ping event', 'client_ip': 'testclient', 'level': 'info', 'timestamp': '2026-03-27T13:41:16.987267Z'}
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 200 OK"
=============================== warnings summary ===============================
tests/test_main.py::test_lifespan_startup_shutdown_configured
  /home/runner/work/stampbot/stampbot/stampbot/webhook_handler.py:116: SyntaxWarning: "is not" with 'str' literal. Did you mean "!="?
    elif event_type is not "issue_comment":

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_valid_ping - AssertionError: assert 'ignored' == 'ok'
  
  - ok
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 101 passed, 3 deselected, 1 warning in 0.86s
operator: core/ReplaceComparisonOperator_Eq_IsNot, occurrence: 3
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -119,7 +119,7 @@
                     result = await self._handle_pr_comment(payload)
                 else:
                     result = {"status": "ignored", "message": "Not a PR comment"}
-            elif event_type == "ping":
+            elif event_type is not "ping":
                 result = {"status": "ok", "message": "pong"}
             else:
                 result = {"status": "ignored", "message": f"Event type {event_type} not handled"}
........................................................................ [ 34%]
........................................................................ [ 68%]
.................F
=================================== FAILURES ===================================
_______________________________ test_ping_event ________________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f8a7c30f110>

    @pytest.mark.asyncio
    async def test_ping_event(webhook_handler):
        """Test handling ping event."""
        payload = {"zen": "Design for failure."}
        result = await webhook_handler.handle_event("ping", payload)
>       assert result["status"] == "ok"
E       AssertionError: assert 'ignored' == 'ok'
E         
E         - ok
E         + ignored

tests/test_webhook_handler.py:46: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:00:56.343234Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'ping', 'action': ''}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'ping', 'action': ''}, 'event': 'Received ping event', 'level': 'info', 'timestamp': '2026-03-27T14:00:56.343234Z'}
=============================== warnings summary ===============================
tests/test_main.py::test_lifespan_startup_shutdown_configured
  /home/runner/work/stampbot/stampbot/stampbot/webhook_handler.py:122: SyntaxWarning: "is not" with 'str' literal. Did you mean "!="?
    elif event_type is not "ping":

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_ping_event - AssertionError: assert 'ignored' == 'ok'
  
  - ok
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 161 passed, 3 deselected, 1 warning in 1.06s
operator: core/ReplaceComparisonOperator_Eq_IsNot, occurrence: 4
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -187,7 +187,7 @@
                     },
                 )
 
-                if action == "opened":
+                if action is not "opened":
                     await run_in_threadpool(
                         github_client.create_pr_review_comment,
                         installation_id,
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................................F
=================================== FAILURES ===================================
_______________ test_invalid_repo_config_no_review_on_non_opened _______________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f7f9bf2d970>
mock_github_client = <MagicMock name='github_client' id='140186124843200'>

    @pytest.mark.asyncio
    async def test_invalid_repo_config_no_review_on_non_opened(webhook_handler, mock_github_client):
        """Test invalid config skips review comment for non-opened events."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
        payload["action"] = "labeled"
        mock_github_client.get_repo_file.return_value = 'chatops_required_permission = "invalid"'
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
        assert result["status"] == "error"
>       mock_github_client.create_pr_review_comment.assert_not_called()

tests/test_webhook_handler.py:532: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <MagicMock name='github_client.create_pr_review_comment' id='140186124844208'>

    def assert_not_called(self):
        """assert that the mock was never called.
        """
        if self.call_count != 0:
            msg = ("Expected '%s' to not have been called. Called %s times.%s"
                   % (self._mock_name or 'mock',
                      self.call_count,
                      self._calls_repr()))
>           raise AssertionError(msg)
E           AssertionError: Expected 'create_pr_review_comment' to not have been called. Called 1 times.
E           Calls: [call(12345, 'octocat/hello-world', 42, 'Stampbot configuration error in `stampbot.toml`:\n\nInvalid chatops_required_permission: invalid. Valid values: none, read, triage, write, maintain, admin\n\nPlease fix the file to re-enable automation.')].

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:946: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:49:24.548868Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'labeled'}
2026-03-27T13:49:24.550189Z [warning  ] Invalid stampbot.toml in octocat/hello-world: Invalid chatops_required_permission: invalid. Valid values: none, read, triage, write, maintain, admin _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=warning extra={'repo': 'octocat/hello-world', 'error': 'Invalid chatops_required_permission: invalid. Valid values: none, read, triage, write, maintain, admin'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'labeled'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:49:24.548868Z'}
WARNING  stampbot.webhook_handler:webhook_handler.py:176 {'extra': {'repo': 'octocat/hello-world', 'error': 'Invalid chatops_required_permission: invalid. Valid values: none, read, triage, write, maintain, admin'}, 'event': 'Invalid stampbot.toml in octocat/hello-world: Invalid chatops_required_permission: invalid. Valid values: none, read, triage, write, maintain, admin', 'level': 'warning', 'timestamp': '2026-03-27T13:49:24.550189Z'}
=============================== warnings summary ===============================
tests/test_main.py::test_lifespan_startup_shutdown_configured
  /home/runner/work/stampbot/stampbot/stampbot/webhook_handler.py:190: SyntaxWarning: "is not" with 'str' literal. Did you mean "!="?
    if action is not "opened":

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_invalid_repo_config_no_review_on_non_opened - AssertionError: Expected 'create_pr_review_comment' to not have been called. Called 1 times.
Calls: [call(12345, 'octocat/hello-world', 42, 'Stampbot configuration error in `stampbot.toml`:\n\nInvalid chatops_required_permission: invalid. Valid values: none, read, triage, write, maintain, admin\n\nPlease fix the file to re-enable automation.')].
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 193 passed, 3 deselected, 1 warning in 1.65s
operator: core/ReplaceComparisonOperator_Eq_IsNot, occurrence: 5
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -206,7 +206,7 @@
                     "message": "Invalid repository configuration",
                 }
 
-            if action == "opened":
+            if action is not "opened":
                 for label in repo_config.approval_labels:
                     label_exists = await run_in_threadpool(
                         github_client.repo_has_label,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
=============================== warnings summary ===============================
tests/test_main.py::test_lifespan_startup_shutdown_configured
  /home/runner/work/stampbot/stampbot/stampbot/webhook_handler.py:209: SyntaxWarning: "is not" with 'str' literal. Did you mean "!="?
    if action is not "opened":

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
211 passed, 3 deselected, 1 warning in 1.64s
operator: core/ReplaceComparisonOperator_Eq_IsNot, occurrence: 6
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -314,7 +314,7 @@
                         }
 
             # Check if we should remove approval when label is removed
-            if repo_config.auto_approve_on_label and action == "unlabeled":
+            if repo_config.auto_approve_on_label and action is not "unlabeled":
                 removed_label = payload.get("label", {}).get("name")
                 if removed_label in repo_config.approval_labels:
                     logger.info(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
=============================== warnings summary ===============================
tests/test_main.py::test_lifespan_startup_shutdown_configured
  /home/runner/work/stampbot/stampbot/stampbot/webhook_handler.py:317: SyntaxWarning: "is not" with 'str' literal. Did you mean "!="?
    if repo_config.auto_approve_on_label and action is not "unlabeled":

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
211 passed, 3 deselected, 1 warning in 1.62s
operator: core/ReplaceComparisonOperator_Eq_IsNot, occurrence: 7
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -572,7 +572,7 @@
 
                 org_repo_full_name = None
                 if (
-                    owner_type == "Organization"
+                    owner_type is not "Organization"
                     and owner_login
                     and repo_full_name != f"{owner_login}/.github"
                 ):
........................................................................ [ 34%]
........................................................................ [ 68%]
............................................F
=================================== FAILURES ===================================
_____________________ test_repo_config_uses_default_branch _____________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f0342ec0e90>
mock_github_client = <MagicMock name='github_client' id='139651983390912'>

    @pytest.mark.asyncio
    async def test_repo_config_uses_default_branch(webhook_handler, mock_github_client):
        """Test repo config is loaded from the default branch."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
        payload["repository"]["default_branch"] = "develop"
        payload["pull_request"]["base"]["ref"] = "release"
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
        assert result["status"] == "success"
        args = mock_github_client.get_repo_file.call_args[0]
>       assert args[3] == "develop"
E       AssertionError: assert None == 'develop'

tests/test_webhook_handler.py:434: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:47:30.283125Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T13:47:30.283810Z [info     ] No stampbot.toml found in octocat/hello-world or octocat/.github, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': 'octocat/.github'}
2026-03-27T13:47:30.284590Z [info     ] PR #42 has approval label: autoapprove _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:47:30.283125Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': 'octocat/.github'}, 'event': 'No stampbot.toml found in octocat/hello-world or octocat/.github, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:47:30.283810Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:278 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'PR #42 has approval label: autoapprove', 'level': 'info', 'timestamp': '2026-03-27T13:47:30.284590Z'}
=============================== warnings summary ===============================
tests/test_main.py::test_lifespan_startup_shutdown_configured
  /home/runner/work/stampbot/stampbot/stampbot/webhook_handler.py:575: SyntaxWarning: "is not" with 'str' literal. Did you mean "!="?
    owner_type is not "Organization"

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_repo_config_uses_default_branch - AssertionError: assert None == 'develop'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 188 passed, 3 deselected, 1 warning in 1.57s
operator: core/ReplaceComparisonOperator_NotEq_Eq, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -574,7 +574,7 @@
                 if (
                     owner_type == "Organization"
                     and owner_login
-                    and repo_full_name != f"{owner_login}/.github"
+                    and repo_full_name == f"{owner_login}/.github"
                 ):
                     org_repo_full_name = f"{owner_login}/.github"
                     org_content = await run_in_threadpool(
........................................................................ [ 34%]
........................................................................ [ 68%]
.............................................F
=================================== FAILURES ===================================
_____________________ test_org_github_repo_config_fallback _____________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f7d3f8a0170>
mock_github_client = <MagicMock name='github_client' id='140175914615536'>

    @pytest.mark.asyncio
    async def test_org_github_repo_config_fallback(webhook_handler, mock_github_client):
        """Test org .github stampbot.toml is used when repo config is missing."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
        payload["repository"]["full_name"] = "acme/widgets"
        payload["repository"]["default_branch"] = "main"
        payload["repository"]["owner"] = {"login": "acme", "type": "Organization"}
        payload["pull_request"]["labels"] = [{"name": "org-approve"}]
    
        def get_repo_file_side_effect(_installation_id, repo_full_name, _file_path, _ref):
            if repo_full_name == "acme/widgets":
                return None
            if repo_full_name == "acme/.github":
                return 'approval_labels = ["org-approve"]'
            return None
    
        mock_github_client.get_repo_file.side_effect = get_repo_file_side_effect
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'ignored' == 'success'
E         
E         - success
E         + ignored

tests/test_webhook_handler.py:457: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:31:39.040594Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T13:31:39.041163Z [info     ] No stampbot.toml found in acme/widgets, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'acme/widgets', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:31:39.040594Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'acme/widgets', 'org_repo': None}, 'event': 'No stampbot.toml found in acme/widgets, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:31:39.041163Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_org_github_repo_config_fallback - AssertionError: assert 'ignored' == 'success'
  
  - success
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 189 passed, 3 deselected in 1.56s
operator: core/ReplaceComparisonOperator_NotEq_Lt, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -574,7 +574,7 @@
                 if (
                     owner_type == "Organization"
                     and owner_login
-                    and repo_full_name != f"{owner_login}/.github"
+                    and repo_full_name < f"{owner_login}/.github"
                 ):
                     org_repo_full_name = f"{owner_login}/.github"
                     org_content = await run_in_threadpool(
........................................................................ [ 34%]
........................................................................ [ 68%]
.............................................F
=================================== FAILURES ===================================
_____________________ test_org_github_repo_config_fallback _____________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f6970673110>
mock_github_client = <MagicMock name='github_client' id='140090835452656'>

    @pytest.mark.asyncio
    async def test_org_github_repo_config_fallback(webhook_handler, mock_github_client):
        """Test org .github stampbot.toml is used when repo config is missing."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
        payload["repository"]["full_name"] = "acme/widgets"
        payload["repository"]["default_branch"] = "main"
        payload["repository"]["owner"] = {"login": "acme", "type": "Organization"}
        payload["pull_request"]["labels"] = [{"name": "org-approve"}]
    
        def get_repo_file_side_effect(_installation_id, repo_full_name, _file_path, _ref):
            if repo_full_name == "acme/widgets":
                return None
            if repo_full_name == "acme/.github":
                return 'approval_labels = ["org-approve"]'
            return None
    
        mock_github_client.get_repo_file.side_effect = get_repo_file_side_effect
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'ignored' == 'success'
E         
E         - success
E         + ignored

tests/test_webhook_handler.py:457: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:58:10.092870Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T13:58:10.093407Z [info     ] No stampbot.toml found in acme/widgets, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'acme/widgets', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:58:10.092870Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'acme/widgets', 'org_repo': None}, 'event': 'No stampbot.toml found in acme/widgets, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:58:10.093407Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_org_github_repo_config_fallback - AssertionError: assert 'ignored' == 'success'
  
  - success
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 189 passed, 3 deselected in 1.56s
operator: core/ReplaceComparisonOperator_NotEq_LtE, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -574,7 +574,7 @@
                 if (
                     owner_type == "Organization"
                     and owner_login
-                    and repo_full_name != f"{owner_login}/.github"
+                    and repo_full_name <= f"{owner_login}/.github"
                 ):
                     org_repo_full_name = f"{owner_login}/.github"
                     org_content = await run_in_threadpool(
........................................................................ [ 34%]
........................................................................ [ 68%]
.............................................F
=================================== FAILURES ===================================
_____________________ test_org_github_repo_config_fallback _____________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f849ec83230>
mock_github_client = <MagicMock name='github_client' id='140207577416432'>

    @pytest.mark.asyncio
    async def test_org_github_repo_config_fallback(webhook_handler, mock_github_client):
        """Test org .github stampbot.toml is used when repo config is missing."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
        payload["repository"]["full_name"] = "acme/widgets"
        payload["repository"]["default_branch"] = "main"
        payload["repository"]["owner"] = {"login": "acme", "type": "Organization"}
        payload["pull_request"]["labels"] = [{"name": "org-approve"}]
    
        def get_repo_file_side_effect(_installation_id, repo_full_name, _file_path, _ref):
            if repo_full_name == "acme/widgets":
                return None
            if repo_full_name == "acme/.github":
                return 'approval_labels = ["org-approve"]'
            return None
    
        mock_github_client.get_repo_file.side_effect = get_repo_file_side_effect
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'ignored' == 'success'
E         
E         - success
E         + ignored

tests/test_webhook_handler.py:457: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:09:39.365697Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T14:09:39.366253Z [info     ] No stampbot.toml found in acme/widgets, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'acme/widgets', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T14:09:39.365697Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'acme/widgets', 'org_repo': None}, 'event': 'No stampbot.toml found in acme/widgets, using defaults', 'level': 'info', 'timestamp': '2026-03-27T14:09:39.366253Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_org_github_repo_config_fallback - AssertionError: assert 'ignored' == 'success'
  
  - success
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 189 passed, 3 deselected in 1.57s
operator: core/ReplaceComparisonOperator_NotEq_Gt, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -574,7 +574,7 @@
                 if (
                     owner_type == "Organization"
                     and owner_login
-                    and repo_full_name != f"{owner_login}/.github"
+                    and repo_full_name > f"{owner_login}/.github"
                 ):
                     org_repo_full_name = f"{owner_login}/.github"
                     org_content = await run_in_threadpool(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceComparisonOperator_NotEq_GtE, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -574,7 +574,7 @@
                 if (
                     owner_type == "Organization"
                     and owner_login
-                    and repo_full_name != f"{owner_login}/.github"
+                    and repo_full_name >= f"{owner_login}/.github"
                 ):
                     org_repo_full_name = f"{owner_login}/.github"
                     org_content = await run_in_threadpool(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceComparisonOperator_NotEq_Is, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -574,7 +574,7 @@
                 if (
                     owner_type == "Organization"
                     and owner_login
-                    and repo_full_name != f"{owner_login}/.github"
+                    and repo_full_name is f"{owner_login}/.github"
                 ):
                     org_repo_full_name = f"{owner_login}/.github"
                     org_content = await run_in_threadpool(
........................................................................ [ 34%]
........................................................................ [ 68%]
.............................................F
=================================== FAILURES ===================================
_____________________ test_org_github_repo_config_fallback _____________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f719ff6b170>
mock_github_client = <MagicMock name='github_client' id='140126061362016'>

    @pytest.mark.asyncio
    async def test_org_github_repo_config_fallback(webhook_handler, mock_github_client):
        """Test org .github stampbot.toml is used when repo config is missing."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
        payload["repository"]["full_name"] = "acme/widgets"
        payload["repository"]["default_branch"] = "main"
        payload["repository"]["owner"] = {"login": "acme", "type": "Organization"}
        payload["pull_request"]["labels"] = [{"name": "org-approve"}]
    
        def get_repo_file_side_effect(_installation_id, repo_full_name, _file_path, _ref):
            if repo_full_name == "acme/widgets":
                return None
            if repo_full_name == "acme/.github":
                return 'approval_labels = ["org-approve"]'
            return None
    
        mock_github_client.get_repo_file.side_effect = get_repo_file_side_effect
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'ignored' == 'success'
E         
E         - success
E         + ignored

tests/test_webhook_handler.py:457: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:02:29.182906Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T14:02:29.183445Z [info     ] No stampbot.toml found in acme/widgets, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'acme/widgets', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T14:02:29.182906Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'acme/widgets', 'org_repo': None}, 'event': 'No stampbot.toml found in acme/widgets, using defaults', 'level': 'info', 'timestamp': '2026-03-27T14:02:29.183445Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_org_github_repo_config_fallback - AssertionError: assert 'ignored' == 'success'
  
  - success
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 189 passed, 3 deselected in 1.58s
operator: core/ReplaceComparisonOperator_NotEq_IsNot, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -574,7 +574,7 @@
                 if (
                     owner_type == "Organization"
                     and owner_login
-                    and repo_full_name != f"{owner_login}/.github"
+                    and repo_full_name is not f"{owner_login}/.github"
                 ):
                     org_repo_full_name = f"{owner_login}/.github"
                     org_content = await run_in_threadpool(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceComparisonOperator_Gt_Eq, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -370,7 +370,7 @@
         comment_body = comment.get("body", "")
 
         # Security: limit comment length to prevent DoS
-        if len(comment_body) > MAX_COMMENT_LENGTH:
+        if len(comment_body) == MAX_COMMENT_LENGTH:
             return {"status": "ignored", "message": "Comment too long"}
 
         comment_body = comment_body.lower().strip()
........................................................................ [ 34%]
........................................................................ [ 68%]
.....................................F
=================================== FAILURES ===================================
_____________________ test_issue_comment_too_long_rejected _____________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f52cd90f020>
mock_github_client = <MagicMock name='github_client' id='139993521528784'>

    @pytest.mark.asyncio
    async def test_issue_comment_too_long_rejected(webhook_handler, mock_github_client):
        """Test that comments exceeding max length are rejected to prevent DoS."""
        payload = load_fixture("issue_comment_approve")
        # Create an excessively long comment (> 64KB)
        payload["comment"]["body"] = "@stampbot approve " + "x" * 100000
    
        result = await webhook_handler.handle_event("issue_comment", payload)
    
>       assert result["status"] == "ignored"
E       AssertionError: assert 'success' == 'ignored'
E         
E         - ignored
E         + success

tests/test_webhook_handler.py:315: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:42:52.103188Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-03-27T13:42:52.103760Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T13:42:52.103188Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:42:52.103760Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_too_long_rejected - AssertionError: assert 'success' == 'ignored'
  
  - ignored
  + success
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 181 passed, 3 deselected in 1.14s
operator: core/ReplaceComparisonOperator_Gt_NotEq, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -370,7 +370,7 @@
         comment_body = comment.get("body", "")
 
         # Security: limit comment length to prevent DoS
-        if len(comment_body) > MAX_COMMENT_LENGTH:
+        if len(comment_body) != MAX_COMMENT_LENGTH:
             return {"status": "ignored", "message": "Comment too long"}
 
         comment_body = comment_body.lower().strip()
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fc243252150>
mock_github_client = <MagicMock name='github_client' id='140472327433792'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
        result = await webhook_handler.handle_event("issue_comment", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'ignored' == 'success'
E         
E         - success
E         + ignored

tests/test_webhook_handler.py:259: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:31:43.331742Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T13:31:43.331742Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - AssertionError: assert 'ignored' == 'success'
  
  - success
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.11s
operator: core/ReplaceComparisonOperator_Gt_Lt, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -370,7 +370,7 @@
         comment_body = comment.get("body", "")
 
         # Security: limit comment length to prevent DoS
-        if len(comment_body) > MAX_COMMENT_LENGTH:
+        if len(comment_body) < MAX_COMMENT_LENGTH:
             return {"status": "ignored", "message": "Comment too long"}
 
         comment_body = comment_body.lower().strip()
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f93aaf62150>
mock_github_client = <MagicMock name='github_client' id='140272205721152'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
        result = await webhook_handler.handle_event("issue_comment", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'ignored' == 'success'
E         
E         - success
E         + ignored

tests/test_webhook_handler.py:259: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:39:27.111538Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T13:39:27.111538Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - AssertionError: assert 'ignored' == 'success'
  
  - success
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.13s
operator: core/ReplaceComparisonOperator_Gt_LtE, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -370,7 +370,7 @@
         comment_body = comment.get("body", "")
 
         # Security: limit comment length to prevent DoS
-        if len(comment_body) > MAX_COMMENT_LENGTH:
+        if len(comment_body) <= MAX_COMMENT_LENGTH:
             return {"status": "ignored", "message": "Comment too long"}
 
         comment_body = comment_body.lower().strip()
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fdda5e33800>
mock_github_client = <MagicMock name='github_client' id='140589936897600'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
        result = await webhook_handler.handle_event("issue_comment", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'ignored' == 'success'
E         
E         - success
E         + ignored

tests/test_webhook_handler.py:259: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:58:15.061878Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T13:58:15.061878Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - AssertionError: assert 'ignored' == 'success'
  
  - success
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.13s
operator: core/ReplaceComparisonOperator_Gt_GtE, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -370,7 +370,7 @@
         comment_body = comment.get("body", "")
 
         # Security: limit comment length to prevent DoS
-        if len(comment_body) > MAX_COMMENT_LENGTH:
+        if len(comment_body) >= MAX_COMMENT_LENGTH:
             return {"status": "ignored", "message": "Comment too long"}
 
         comment_body = comment_body.lower().strip()
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceComparisonOperator_Gt_Is, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -370,7 +370,7 @@
         comment_body = comment.get("body", "")
 
         # Security: limit comment length to prevent DoS
-        if len(comment_body) > MAX_COMMENT_LENGTH:
+        if len(comment_body) is MAX_COMMENT_LENGTH:
             return {"status": "ignored", "message": "Comment too long"}
 
         comment_body = comment_body.lower().strip()
........................................................................ [ 34%]
........................................................................ [ 68%]
.....................................F
=================================== FAILURES ===================================
_____________________ test_issue_comment_too_long_rejected _____________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fb9de47f020>
mock_github_client = <MagicMock name='github_client' id='140436185046992'>

    @pytest.mark.asyncio
    async def test_issue_comment_too_long_rejected(webhook_handler, mock_github_client):
        """Test that comments exceeding max length are rejected to prevent DoS."""
        payload = load_fixture("issue_comment_approve")
        # Create an excessively long comment (> 64KB)
        payload["comment"]["body"] = "@stampbot approve " + "x" * 100000
    
        result = await webhook_handler.handle_event("issue_comment", payload)
    
>       assert result["status"] == "ignored"
E       AssertionError: assert 'success' == 'ignored'
E         
E         - ignored
E         + success

tests/test_webhook_handler.py:315: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:52:39.565499Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-03-27T13:52:39.566096Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T13:52:39.565499Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:52:39.566096Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_too_long_rejected - AssertionError: assert 'success' == 'ignored'
  
  - ignored
  + success
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 181 passed, 3 deselected in 1.15s
operator: core/ReplaceComparisonOperator_Gt_IsNot, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -370,7 +370,7 @@
         comment_body = comment.get("body", "")
 
         # Security: limit comment length to prevent DoS
-        if len(comment_body) > MAX_COMMENT_LENGTH:
+        if len(comment_body) is not MAX_COMMENT_LENGTH:
             return {"status": "ignored", "message": "Comment too long"}
 
         comment_body = comment_body.lower().strip()
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fe75dd5a150>
mock_github_client = <MagicMock name='github_client' id='140631688998464'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
        result = await webhook_handler.handle_event("issue_comment", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'ignored' == 'success'
E         
E         - success
E         + ignored

tests/test_webhook_handler.py:259: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:09:21.185579Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T14:09:21.185579Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - AssertionError: assert 'ignored' == 'success'
  
  - success
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.12s
operator: core/ReplaceComparisonOperator_Is_Eq, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -214,7 +214,7 @@
                         repo_full_name,
                         label,
                     )
-                    if label_exists is False:
+                    if label_exists == False:
                         logger.warning(
                             "Approval label %s not found in %s",
                             label,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceComparisonOperator_Is_NotEq, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -214,7 +214,7 @@
                         repo_full_name,
                         label,
                     )
-                    if label_exists is False:
+                    if label_exists != False:
                         logger.warning(
                             "Approval label %s not found in %s",
                             label,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceComparisonOperator_Is_Lt, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -214,7 +214,7 @@
                         repo_full_name,
                         label,
                     )
-                    if label_exists is False:
+                    if label_exists < False:
                         logger.warning(
                             "Approval label %s not found in %s",
                             label,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceComparisonOperator_Is_LtE, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -214,7 +214,7 @@
                         repo_full_name,
                         label,
                     )
-                    if label_exists is False:
+                    if label_exists <= False:
                         logger.warning(
                             "Approval label %s not found in %s",
                             label,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceComparisonOperator_Is_Gt, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -214,7 +214,7 @@
                         repo_full_name,
                         label,
                     )
-                    if label_exists is False:
+                    if label_exists > False:
                         logger.warning(
                             "Approval label %s not found in %s",
                             label,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.64s
operator: core/ReplaceComparisonOperator_Is_GtE, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -214,7 +214,7 @@
                         repo_full_name,
                         label,
                     )
-                    if label_exists is False:
+                    if label_exists >= False:
                         logger.warning(
                             "Approval label %s not found in %s",
                             label,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceComparisonOperator_Is_IsNot, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -49,7 +49,7 @@
         Raises:
             RuntimeError: If webhook secret is not configured.
         """
-        if self._webhook_secret is None:
+        if self._webhook_secret is not None:
             if not settings.webhook_secret:
                 raise RuntimeError(
                     "Webhook secret not configured. Visit /setup to create your GitHub App."
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7f9c32b9f770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
>       response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )

tests/test_main.py:69: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:546: in post
    return super().post(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1144: in post
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:296: in webhook
    if not webhook_handler.verify_signature(body, x_hub_signature_256):
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/webhook_handler.py:74: in verify_signature
    "sha256=" + hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/hmac.py:218: in new
    return HMAC(key, msg, digestmod)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <hmac.HMAC object at 0x7f9c329b1940>, key = None
msg = b'{"test": "data"}', digestmod = <built-in function openssl_sha256>

    def __init__(self, key, msg=None, digestmod=''):
        """Create a new HMAC object.
    
        key: bytes or buffer, key for the keyed hash object.
        msg: bytes or buffer, Initial input for the hash or None.
        digestmod: A hash name suitable for hashlib.new(). *OR*
                   A hashlib constructor returning a new hash object. *OR*
                   A module supporting PEP 247.
    
                   Required as of 3.8, despite its position after the optional
                   msg argument.  Passing it as a keyword argument is
                   recommended, though not required for legacy API reasons.
        """
    
        if not isinstance(key, (bytes, bytearray)):
>           raise TypeError(f"key: expected bytes or bytearray, "
                            f"but got {type(key).__name__!r}")
E           TypeError: key: expected bytes or bytearray, but got 'NoneType'

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/hmac.py:71: TypeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - TypeError: key: expected bytes or bytearray, but got 'NoneType'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 1.17s
operator: core/ReplaceComparisonOperator_Is_IsNot, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -214,7 +214,7 @@
                         repo_full_name,
                         label,
                     )
-                    if label_exists is False:
+                    if label_exists is not False:
                         logger.warning(
                             "Approval label %s not found in %s",
                             label,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceUnaryOperator_USub_UAdd, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -383,7 +383,7 @@
         if "pull_request" in payload.get("issue", {}):
             pr_url = payload["issue"]["pull_request"]["url"]
             # Extract PR number from URL
-            pr_number = int(pr_url.split("/")[-1])
+            pr_number = int(pr_url.split("/")[+1])
         elif "pull_request" in payload:
             pr_number = payload["pull_request"]["number"]
         else:
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f57a541a620>
mock_github_client = <MagicMock name='github_client' id='140014400695872'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
>       result = await webhook_handler.handle_event("issue_comment", payload)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_webhook_handler.py:257: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/webhook_handler.py:119: in handle_event
    result = await self._handle_pr_comment(payload)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7f57a541a620>
payload = {'action': 'created', 'comment': {'body': '@stampbot approve\n\nLGTM, approving this PR.', 'created_at': '2025-01-10T1...U='}, 'issue': {'assignee': None, 'assignees': [], 'body': 'This PR adds a new feature.', 'closed_at': None, ...}, ...}

    async def _handle_pr_comment(self, payload: dict[str, Any]) -> dict[str, Any]:
        """Handle PR comment events for chatops.
    
        Args:
            payload: Event payload
    
        Returns:
            Response dictionary
        """
        comment = payload.get("comment", {})
        comment_body = comment.get("body", "")
    
        # Security: limit comment length to prevent DoS
        if len(comment_body) > MAX_COMMENT_LENGTH:
            return {"status": "ignored", "message": "Comment too long"}
    
        comment_body = comment_body.lower().strip()
    
        # Check if bot is mentioned
        if "@stampbot" not in comment_body:
            return {"status": "ignored", "message": "Bot not mentioned"}
    
        # Extract PR info
        if "pull_request" in payload.get("issue", {}):
            pr_url = payload["issue"]["pull_request"]["url"]
            # Extract PR number from URL
>           pr_number = int(pr_url.split("/")[+1])
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^
E           ValueError: invalid literal for int() with base 10: ''

stampbot/webhook_handler.py:386: ValueError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:33:07.216579Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T13:33:07.216579Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - ValueError: invalid literal for int() with base 10: ''
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.16s
operator: core/ReplaceUnaryOperator_USub_Invert, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -383,7 +383,7 @@
         if "pull_request" in payload.get("issue", {}):
             pr_url = payload["issue"]["pull_request"]["url"]
             # Extract PR number from URL
-            pr_number = int(pr_url.split("/")[-1])
+            pr_number = int(pr_url.split("/")[~1])
         elif "pull_request" in payload:
             pr_number = payload["pull_request"]["number"]
         else:
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fdd8ed4a150>
mock_github_client = <MagicMock name='github_client' id='140589561343552'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
>       result = await webhook_handler.handle_event("issue_comment", payload)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_webhook_handler.py:257: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/webhook_handler.py:119: in handle_event
    result = await self._handle_pr_comment(payload)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7fdd8ed4a150>
payload = {'action': 'created', 'comment': {'body': '@stampbot approve\n\nLGTM, approving this PR.', 'created_at': '2025-01-10T1...U='}, 'issue': {'assignee': None, 'assignees': [], 'body': 'This PR adds a new feature.', 'closed_at': None, ...}, ...}

    async def _handle_pr_comment(self, payload: dict[str, Any]) -> dict[str, Any]:
        """Handle PR comment events for chatops.
    
        Args:
            payload: Event payload
    
        Returns:
            Response dictionary
        """
        comment = payload.get("comment", {})
        comment_body = comment.get("body", "")
    
        # Security: limit comment length to prevent DoS
        if len(comment_body) > MAX_COMMENT_LENGTH:
            return {"status": "ignored", "message": "Comment too long"}
    
        comment_body = comment_body.lower().strip()
    
        # Check if bot is mentioned
        if "@stampbot" not in comment_body:
            return {"status": "ignored", "message": "Bot not mentioned"}
    
        # Extract PR info
        if "pull_request" in payload.get("issue", {}):
            pr_url = payload["issue"]["pull_request"]["url"]
            # Extract PR number from URL
>           pr_number = int(pr_url.split("/")[~1])
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^
E           ValueError: invalid literal for int() with base 10: 'pulls'

stampbot/webhook_handler.py:386: ValueError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:00:14.328589Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T14:00:14.328589Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - ValueError: invalid literal for int() with base 10: 'pulls'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.17s
operator: core/ReplaceUnaryOperator_USub_Not, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -383,7 +383,7 @@
         if "pull_request" in payload.get("issue", {}):
             pr_url = payload["issue"]["pull_request"]["url"]
             # Extract PR number from URL
-            pr_number = int(pr_url.split("/")[-1])
+            pr_number = int(pr_url.split("/")[not 1])
         elif "pull_request" in payload:
             pr_number = payload["pull_request"]["number"]
         else:
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fa1d8962150>
mock_github_client = <MagicMock name='github_client' id='140333100723776'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
>       result = await webhook_handler.handle_event("issue_comment", payload)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_webhook_handler.py:257: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/webhook_handler.py:119: in handle_event
    result = await self._handle_pr_comment(payload)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7fa1d8962150>
payload = {'action': 'created', 'comment': {'body': '@stampbot approve\n\nLGTM, approving this PR.', 'created_at': '2025-01-10T1...U='}, 'issue': {'assignee': None, 'assignees': [], 'body': 'This PR adds a new feature.', 'closed_at': None, ...}, ...}

    async def _handle_pr_comment(self, payload: dict[str, Any]) -> dict[str, Any]:
        """Handle PR comment events for chatops.
    
        Args:
            payload: Event payload
    
        Returns:
            Response dictionary
        """
        comment = payload.get("comment", {})
        comment_body = comment.get("body", "")
    
        # Security: limit comment length to prevent DoS
        if len(comment_body) > MAX_COMMENT_LENGTH:
            return {"status": "ignored", "message": "Comment too long"}
    
        comment_body = comment_body.lower().strip()
    
        # Check if bot is mentioned
        if "@stampbot" not in comment_body:
            return {"status": "ignored", "message": "Bot not mentioned"}
    
        # Extract PR info
        if "pull_request" in payload.get("issue", {}):
            pr_url = payload["issue"]["pull_request"]["url"]
            # Extract PR number from URL
>           pr_number = int(pr_url.split("/")[not 1])
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E           ValueError: invalid literal for int() with base 10: 'https:'

stampbot/webhook_handler.py:386: ValueError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:27:40.625961Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T13:27:40.625961Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - ValueError: invalid literal for int() with base 10: 'https:'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.16s
operator: core/ReplaceUnaryOperator_Delete_USub, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -383,7 +383,7 @@
         if "pull_request" in payload.get("issue", {}):
             pr_url = payload["issue"]["pull_request"]["url"]
             # Extract PR number from URL
-            pr_number = int(pr_url.split("/")[-1])
+            pr_number = int(pr_url.split("/")[1])
         elif "pull_request" in payload:
             pr_number = payload["pull_request"]["number"]
         else:
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7efe3181b070>
mock_github_client = <MagicMock name='github_client' id='139630206643776'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
>       result = await webhook_handler.handle_event("issue_comment", payload)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_webhook_handler.py:257: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/webhook_handler.py:119: in handle_event
    result = await self._handle_pr_comment(payload)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7efe3181b070>
payload = {'action': 'created', 'comment': {'body': '@stampbot approve\n\nLGTM, approving this PR.', 'created_at': '2025-01-10T1...U='}, 'issue': {'assignee': None, 'assignees': [], 'body': 'This PR adds a new feature.', 'closed_at': None, ...}, ...}

    async def _handle_pr_comment(self, payload: dict[str, Any]) -> dict[str, Any]:
        """Handle PR comment events for chatops.
    
        Args:
            payload: Event payload
    
        Returns:
            Response dictionary
        """
        comment = payload.get("comment", {})
        comment_body = comment.get("body", "")
    
        # Security: limit comment length to prevent DoS
        if len(comment_body) > MAX_COMMENT_LENGTH:
            return {"status": "ignored", "message": "Comment too long"}
    
        comment_body = comment_body.lower().strip()
    
        # Check if bot is mentioned
        if "@stampbot" not in comment_body:
            return {"status": "ignored", "message": "Bot not mentioned"}
    
        # Extract PR info
        if "pull_request" in payload.get("issue", {}):
            pr_url = payload["issue"]["pull_request"]["url"]
            # Extract PR number from URL
>           pr_number = int(pr_url.split("/")[1])
                        ^^^^^^^^^^^^^^^^^^^^^^^^^
E           ValueError: invalid literal for int() with base 10: ''

stampbot/webhook_handler.py:386: ValueError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:10:38.984856Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T14:10:38.984856Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - ValueError: invalid literal for int() with base 10: ''
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.22s
operator: core/ReplaceUnaryOperator_Delete_Not, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -50,7 +50,7 @@
             RuntimeError: If webhook secret is not configured.
         """
         if self._webhook_secret is None:
-            if not settings.webhook_secret:
+            if  settings.webhook_secret:
                 raise RuntimeError(
                     "Webhook secret not configured. Visit /setup to create your GitHub App."
                 )
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7fedbb4b3770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
>       response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )

tests/test_main.py:69: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:546: in post
    return super().post(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1144: in post
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:296: in webhook
    if not webhook_handler.verify_signature(body, x_hub_signature_256):
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/webhook_handler.py:74: in verify_signature
    "sha256=" + hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
                         ^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7fedbb4bacf0>

    @property
    def webhook_secret(self) -> bytes:
        """Get webhook secret, initializing if needed.
    
        Returns:
            Webhook secret as bytes.
    
        Raises:
            RuntimeError: If webhook secret is not configured.
        """
        if self._webhook_secret is None:
            if  settings.webhook_secret:
>               raise RuntimeError(
                    "Webhook secret not configured. Visit /setup to create your GitHub App."
                )
E               RuntimeError: Webhook secret not configured. Visit /setup to create your GitHub App.

stampbot/webhook_handler.py:54: RuntimeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - RuntimeError: Webhook secret not configured. Visit /setup to create your GitHub App.
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 1.17s
operator: core/ReplaceUnaryOperator_Delete_Not, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -67,7 +67,7 @@
         Returns:
             True if signature is valid
         """
-        if not signature:
+        if  signature:
             return False
 
         expected_signature = (
........................................................................ [ 34%]
.........................F
=================================== FAILURES ===================================
________________________ test_webhook_missing_signature ________________________

test_client = <starlette.testclient.TestClient object at 0x7f3f1aadc490>

    def test_webhook_missing_signature(test_client: TestClient):
        """Test webhook rejects requests without signature."""
>       response = test_client.post(
            "/webhook",
            json={"zen": "test"},
            headers={"X-GitHub-Event": "ping"},
        )

tests/test_main.py:84: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:546: in post
    return super().post(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1144: in post
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:296: in webhook
    if not webhook_handler.verify_signature(body, x_hub_signature_256):
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7f3f1acb6cf0>
payload = b'{"zen":"test"}', signature = None

    def verify_signature(self, payload: bytes, signature: str) -> bool:
        """Verify GitHub webhook signature.
    
        Args:
            payload: Request payload
            signature: X-Hub-Signature-256 header value
    
        Returns:
            True if signature is valid
        """
        if  signature:
            return False
    
        expected_signature = (
            "sha256=" + hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
        )
    
>       return hmac.compare_digest(expected_signature, signature)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E       TypeError: unsupported operand types(s) or combination of types: 'str' and 'NoneType'

stampbot/webhook_handler.py:77: TypeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_missing_signature - TypeError: unsupported operand types(s) or combination of types: 'str' and 'NoneType'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 97 passed, 3 deselected in 1.17s
operator: core/ReplaceUnaryOperator_Delete_Not, occurrence: 2
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -157,7 +157,7 @@
                 "github.action": action or "unknown",
             },
         ) as span:
-            if not all([pr_number, repo_full_name, installation_id]):
+            if  all([pr_number, repo_full_name, installation_id]):
                 logger.warning("Missing required fields in PR event")
                 add_span_attributes(span, {"webhook.result": "missing_fields"})
                 set_span_ok(span)
........................................................................ [ 34%]
........................................................................ [ 68%]
...................F
=================================== FAILURES ===================================
____________________ test_pr_opened_with_autoapprove_label _____________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fca6630c050>
mock_github_client = <MagicMock name='github_client' id='140507275166784'>

    @pytest.mark.asyncio
    async def test_pr_opened_with_autoapprove_label(webhook_handler, mock_github_client):
        """Test PR opened with autoapprove label triggers approval."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'error' == 'success'
E         
E         - success
E         + error

tests/test_webhook_handler.py:70: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:49:37.591775Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T13:49:37.591993Z [warning  ] Missing required fields in PR event _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=warning
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:49:37.591775Z'}
WARNING  stampbot.webhook_handler:webhook_handler.py:161 {'event': 'Missing required fields in PR event', 'level': 'warning', 'timestamp': '2026-03-27T13:49:37.591993Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_opened_with_autoapprove_label - AssertionError: assert 'error' == 'success'
  
  - success
  + error
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 163 passed, 3 deselected in 1.08s
operator: core/ReplaceUnaryOperator_Delete_Not, occurrence: 3
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -250,7 +250,7 @@
                         is_eligible, reason = repo_config.is_pr_eligible(
                             labels, pr_title, pr_author, author_team_slugs
                         )
-                        if not is_eligible:
+                        if  is_eligible:
                             logger.info(
                                 "PR #%d not eligible for auto-approval: %s",
                                 pr_number,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................F
=================================== FAILURES ===================================
____________________ test_pr_opened_with_autoapprove_label _____________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fb1f3f04050>
mock_github_client = <MagicMock name='github_client' id='140402279121984'>

    @pytest.mark.asyncio
    async def test_pr_opened_with_autoapprove_label(webhook_handler, mock_github_client):
        """Test PR opened with autoapprove label triggers approval."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'ignored' == 'success'
E         
E         - success
E         + ignored

tests/test_webhook_handler.py:70: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:30:48.115982Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T13:30:48.116629Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T13:30:48.117483Z [info     ] PR #42 not eligible for auto-approval: None _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'pr_author': 'contributor', 'reason': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:30:48.115982Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:30:48.116629Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:254 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'pr_author': 'contributor', 'reason': None}, 'event': 'PR #42 not eligible for auto-approval: None', 'level': 'info', 'timestamp': '2026-03-27T13:30:48.117483Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_opened_with_autoapprove_label - AssertionError: assert 'ignored' == 'success'
  
  - success
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 163 passed, 3 deselected in 1.07s
operator: core/ReplaceUnaryOperator_Delete_Not, occurrence: 4
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -406,7 +406,7 @@
                 "chatops.commenter": commenter,
             },
         ) as span:
-            if not all([pr_number, repo_full_name, installation_id]):
+            if  all([pr_number, repo_full_name, installation_id]):
                 logger.warning("Missing required fields in comment event")
                 add_span_attributes(span, {"chatops.result": "missing_fields"})
                 set_span_ok(span)
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f2efef7e150>
mock_github_client = <MagicMock name='github_client' id='139839821344320'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
        result = await webhook_handler.handle_event("issue_comment", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'error' == 'success'
E         
E         - success
E         + error

tests/test_webhook_handler.py:259: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:30:04.368545Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-03-27T13:30:04.368741Z [warning  ] Missing required fields in comment event _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=warning
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T13:30:04.368545Z'}
WARNING  stampbot.webhook_handler:webhook_handler.py:410 {'event': 'Missing required fields in comment event', 'level': 'warning', 'timestamp': '2026-03-27T13:30:04.368741Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - AssertionError: assert 'error' == 'success'
  
  - success
  + error
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.14s
operator: core/ReplaceUnaryOperator_Delete_Not, occurrence: 5
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -435,7 +435,7 @@
                     "message": "Invalid repository configuration",
                 }
 
-            if not repo_config.chatops_enabled:
+            if  repo_config.chatops_enabled:
                 add_span_attributes(span, {"chatops.result": "disabled"})
                 set_span_ok(span)
                 return {"status": "ignored", "message": "Chatops not enabled"}
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7ff7d33f2200>
mock_github_client = <MagicMock name='github_client' id='140702364970560'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
        result = await webhook_handler.handle_event("issue_comment", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'ignored' == 'success'
E         
E         - success
E         + ignored

tests/test_webhook_handler.py:259: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:40:35.655282Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-03-27T13:40:35.655833Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T13:40:35.655282Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:40:35.655833Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - AssertionError: assert 'ignored' == 'success'
  
  - success
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.11s
operator: core/ReplaceUnaryOperator_Delete_Not, occurrence: 6
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -442,7 +442,7 @@
 
             # Parse command
             command_match = re.search(r"@stampbot\s+(\w+)", comment_body)
-            if not command_match:
+            if  command_match:
                 chatops_commands_total.labels(command="none", status="ignored").inc()
                 add_span_attributes(span, {"chatops.result": "no_command"})
                 set_span_ok(span)
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f8a88616620>
mock_github_client = <MagicMock name='github_client' id='140232970055232'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
        result = await webhook_handler.handle_event("issue_comment", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'ignored' == 'success'
E         
E         - success
E         + ignored

tests/test_webhook_handler.py:259: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:38:46.189603Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-03-27T13:38:46.190183Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T13:38:46.189603Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:38:46.190183Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - AssertionError: assert 'ignored' == 'success'
  
  - success
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.12s
operator: core/ReplaceUnaryOperator_Delete_Not, occurrence: 7
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -459,7 +459,7 @@
                     commenter,
                     repo_config.chatops_required_permission,
                 )
-                if not has_permission:
+                if  has_permission:
                     chatops_commands_total.labels(command=command, status="forbidden").inc()
                     add_span_attributes(
                         span,
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f7540a6e150>
mock_github_client = <MagicMock name='github_client' id='140141573123648'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
        result = await webhook_handler.handle_event("issue_comment", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'ignored' == 'success'
E         
E         - success
E         + ignored

tests/test_webhook_handler.py:259: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:36:15.319016Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-03-27T13:36:15.319569Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T13:36:15.319016Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:36:15.319569Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - AssertionError: assert 'ignored' == 'success'
  
  - success
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.12s
operator: core/ReplaceUnaryOperator_Delete_Not, occurrence: 8
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -729,7 +729,7 @@
                 status=status,
             ).inc()
 
-            if not success:
+            if  success:
                 errors_total.labels(error_type="approval_failed").inc()
 
             add_span_attributes(span, {"approval.result": status})
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/ReplaceUnaryOperator_Delete_Not, occurrence: 9
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -778,7 +778,7 @@
 
                 add_span_attributes(span, {"dismissal.reviews_found": len(review_ids)})
 
-                if not review_ids:
+                if  review_ids:
                     logger.info(
                         "No bot approvals found on PR #%d",
                         pr_number,
........................................................................ [ 34%]
........................................................................ [ 68%]
.........................F
=================================== FAILURES ===================================
________________________ test_pr_unlabeled_autoapprove _________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f330cdf1450>
mock_github_client = <MagicMock name='github_client' id='139857298119984'>

    @pytest.mark.asyncio
    async def test_pr_unlabeled_autoapprove(webhook_handler, mock_github_client):
        """Test removing autoapprove label dismisses approvals."""
        payload = load_fixture("pr_unlabeled_autoapprove")
        mock_github_client.find_bot_reviews.return_value = [111, 222]  # Mock review IDs
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
        assert result["status"] == "success"
        assert "dismissed" in result["message"].lower()
        mock_github_client.find_bot_reviews.assert_called_once()
>       assert mock_github_client.dismiss_approval.call_count == 2
E       AssertionError: assert 0 == 2
E        +  where 0 = <MagicMock name='github_client.dismiss_approval' id='139857298121328'>.call_count
E        +    where <MagicMock name='github_client.dismiss_approval' id='139857298121328'> = <MagicMock name='github_client' id='139857298119984'>.dismiss_approval

tests/test_webhook_handler.py:155: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:57:04.462294Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-03-27T13:57:04.462845Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T13:57:04.463365Z [info     ] Approval label autoapprove removed from PR #42 _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
2026-03-27T13:57:04.463757Z [info     ] No bot approvals found on PR #42 _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'unlabeled'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:57:04.462294Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:57:04.462845Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:320 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'Approval label autoapprove removed from PR #42', 'level': 'info', 'timestamp': '2026-03-27T13:57:04.463365Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:782 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42}, 'event': 'No bot approvals found on PR #42', 'level': 'info', 'timestamp': '2026-03-27T13:57:04.463757Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_unlabeled_autoapprove - AssertionError: assert 0 == 2
 +  where 0 = <MagicMock name='github_client.dismiss_approval' id='139857298121328'>.call_count
 +    where <MagicMock name='github_client.dismiss_approval' id='139857298121328'> = <MagicMock name='github_client' id='139857298119984'>.dismiss_approval
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 169 passed, 3 deselected in 1.09s
operator: core/AddNot, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -49,7 +49,7 @@
         Raises:
             RuntimeError: If webhook secret is not configured.
         """
-        if self._webhook_secret is None:
+        if not self._webhook_secret is None:
             if not settings.webhook_secret:
                 raise RuntimeError(
                     "Webhook secret not configured. Visit /setup to create your GitHub App."
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7f3815ab7770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
>       response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )

tests/test_main.py:69: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:546: in post
    return super().post(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1144: in post
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:296: in webhook
    if not webhook_handler.verify_signature(body, x_hub_signature_256):
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/webhook_handler.py:74: in verify_signature
    "sha256=" + hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/hmac.py:218: in new
    return HMAC(key, msg, digestmod)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <hmac.HMAC object at 0x7f38158c9a80>, key = None
msg = b'{"test": "data"}', digestmod = <built-in function openssl_sha256>

    def __init__(self, key, msg=None, digestmod=''):
        """Create a new HMAC object.
    
        key: bytes or buffer, key for the keyed hash object.
        msg: bytes or buffer, Initial input for the hash or None.
        digestmod: A hash name suitable for hashlib.new(). *OR*
                   A hashlib constructor returning a new hash object. *OR*
                   A module supporting PEP 247.
    
                   Required as of 3.8, despite its position after the optional
                   msg argument.  Passing it as a keyword argument is
                   recommended, though not required for legacy API reasons.
        """
    
        if not isinstance(key, (bytes, bytearray)):
>           raise TypeError(f"key: expected bytes or bytearray, "
                            f"but got {type(key).__name__!r}")
E           TypeError: key: expected bytes or bytearray, but got 'NoneType'

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/hmac.py:71: TypeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - TypeError: key: expected bytes or bytearray, but got 'NoneType'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 1.18s
operator: core/AddNot, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -50,7 +50,7 @@
             RuntimeError: If webhook secret is not configured.
         """
         if self._webhook_secret is None:
-            if not settings.webhook_secret:
+            if not not settings.webhook_secret:
                 raise RuntimeError(
                     "Webhook secret not configured. Visit /setup to create your GitHub App."
                 )
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7f29ed197770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
>       response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )

tests/test_main.py:69: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:546: in post
    return super().post(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1144: in post
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:296: in webhook
    if not webhook_handler.verify_signature(body, x_hub_signature_256):
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/webhook_handler.py:74: in verify_signature
    "sha256=" + hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
                         ^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7f29ed19ecf0>

    @property
    def webhook_secret(self) -> bytes:
        """Get webhook secret, initializing if needed.
    
        Returns:
            Webhook secret as bytes.
    
        Raises:
            RuntimeError: If webhook secret is not configured.
        """
        if self._webhook_secret is None:
            if not not settings.webhook_secret:
>               raise RuntimeError(
                    "Webhook secret not configured. Visit /setup to create your GitHub App."
                )
E               RuntimeError: Webhook secret not configured. Visit /setup to create your GitHub App.

stampbot/webhook_handler.py:54: RuntimeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - RuntimeError: Webhook secret not configured. Visit /setup to create your GitHub App.
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 1.17s
operator: core/AddNot, occurrence: 2
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -67,7 +67,7 @@
         Returns:
             True if signature is valid
         """
-        if not signature:
+        if not not signature:
             return False
 
         expected_signature = (
........................................................................ [ 34%]
.........................F
=================================== FAILURES ===================================
________________________ test_webhook_missing_signature ________________________

test_client = <starlette.testclient.TestClient object at 0x7f57109d8490>

    def test_webhook_missing_signature(test_client: TestClient):
        """Test webhook rejects requests without signature."""
>       response = test_client.post(
            "/webhook",
            json={"zen": "test"},
            headers={"X-GitHub-Event": "ping"},
        )

tests/test_main.py:84: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:546: in post
    return super().post(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1144: in post
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:296: in webhook
    if not webhook_handler.verify_signature(body, x_hub_signature_256):
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7f5710bb6cf0>
payload = b'{"zen":"test"}', signature = None

    def verify_signature(self, payload: bytes, signature: str) -> bool:
        """Verify GitHub webhook signature.
    
        Args:
            payload: Request payload
            signature: X-Hub-Signature-256 header value
    
        Returns:
            True if signature is valid
        """
        if not not signature:
            return False
    
        expected_signature = (
            "sha256=" + hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
        )
    
>       return hmac.compare_digest(expected_signature, signature)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E       TypeError: unsupported operand types(s) or combination of types: 'str' and 'NoneType'

stampbot/webhook_handler.py:77: TypeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_missing_signature - TypeError: unsupported operand types(s) or combination of types: 'str' and 'NoneType'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 97 passed, 3 deselected in 1.18s
operator: core/AddNot, occurrence: 3
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -109,7 +109,7 @@
             )
 
             # Route to appropriate handler
-            if event_type == "pull_request":
+            if not event_type == "pull_request":
                 result = await self._handle_pull_request(payload)
             elif event_type == "pull_request_review_comment":
                 result = await self._handle_pr_comment(payload)
........................................................................ [ 34%]
.............................F
=================================== FAILURES ===================================
___________________________ test_webhook_valid_ping ____________________________

test_client = <starlette.testclient.TestClient object at 0x7f0ec19de8b0>

    def test_webhook_valid_ping(test_client: TestClient):
        """Test webhook accepts valid ping event."""
        import hashlib
        import hmac
    
        from stampbot.webhook_handler import webhook_handler
    
        body = json.dumps({"zen": "Design for failure."}).encode()
        signature = (
            "sha256="
            + hmac.new(
                webhook_handler.webhook_secret,
                body,
                hashlib.sha256,
            ).hexdigest()
        )
    
        response = test_client.post(
            "/webhook",
            content=body,
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": signature,
            },
        )
        assert response.status_code == 200
        data = response.json()
>       assert data["status"] == "ok"
E       AssertionError: assert 'error' == 'ok'
E         
E         - ok
E         + error

tests/test_main.py:177: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:12:30.480709Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-03-27T14:12:30.480968Z [warning  ] Missing required fields in PR event _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=warning client_ip=testclient
2026-03-27T14:12:30.481817Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 200 OK"
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'ping', 'action': ''}, 'event': 'Received ping event', 'client_ip': 'testclient', 'level': 'info', 'timestamp': '2026-03-27T14:12:30.480709Z'}
WARNING  stampbot.webhook_handler:webhook_handler.py:161 {'event': 'Missing required fields in PR event', 'client_ip': 'testclient', 'level': 'warning', 'timestamp': '2026-03-27T14:12:30.480968Z'}
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 200 OK"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_valid_ping - AssertionError: assert 'error' == 'ok'
  
  - ok
  + error
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 101 passed, 3 deselected in 0.85s
operator: core/AddNot, occurrence: 4
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -115,7 +115,7 @@
                 result = await self._handle_pr_comment(payload)
             elif event_type == "issue_comment":
                 # Issue comments can be on PRs too
-                if "pull_request" in payload.get("issue", {}):
+                if not "pull_request" in payload.get("issue", {}):
                     result = await self._handle_pr_comment(payload)
                 else:
                     result = {"status": "ignored", "message": "Not a PR comment"}
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f81ec917070>
mock_github_client = <MagicMock name='github_client' id='140195996216896'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
        result = await webhook_handler.handle_event("issue_comment", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'ignored' == 'success'
E         
E         - success
E         + ignored

tests/test_webhook_handler.py:259: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:51:36.112569Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T13:51:36.112569Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - AssertionError: assert 'ignored' == 'success'
  
  - success
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.13s
operator: core/AddNot, occurrence: 5
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -157,7 +157,7 @@
                 "github.action": action or "unknown",
             },
         ) as span:
-            if not all([pr_number, repo_full_name, installation_id]):
+            if not not all([pr_number, repo_full_name, installation_id]):
                 logger.warning("Missing required fields in PR event")
                 add_span_attributes(span, {"webhook.result": "missing_fields"})
                 set_span_ok(span)
........................................................................ [ 34%]
........................................................................ [ 68%]
...................F
=================================== FAILURES ===================================
____________________ test_pr_opened_with_autoapprove_label _____________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f9de8400050>
mock_github_client = <MagicMock name='github_client' id='140316183675968'>

    @pytest.mark.asyncio
    async def test_pr_opened_with_autoapprove_label(webhook_handler, mock_github_client):
        """Test PR opened with autoapprove label triggers approval."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'error' == 'success'
E         
E         - success
E         + error

tests/test_webhook_handler.py:70: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:02:41.978644Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T14:02:41.978855Z [warning  ] Missing required fields in PR event _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=warning
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T14:02:41.978644Z'}
WARNING  stampbot.webhook_handler:webhook_handler.py:161 {'event': 'Missing required fields in PR event', 'level': 'warning', 'timestamp': '2026-03-27T14:02:41.978855Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_opened_with_autoapprove_label - AssertionError: assert 'error' == 'success'
  
  - success
  + error
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 163 passed, 3 deselected in 1.06s
operator: core/AddNot, occurrence: 6
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -172,7 +172,7 @@
                 owner_type,
             )
 
-            if repo_config.config_error:
+            if not repo_config.config_error:
                 logger.warning(
                     "Invalid stampbot.toml in %s: %s",
                     repo_full_name,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................F
=================================== FAILURES ===================================
____________________ test_pr_opened_with_autoapprove_label _____________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fa74f2f0050>
mock_github_client = <MagicMock name='github_client' id='140356563240000'>

    @pytest.mark.asyncio
    async def test_pr_opened_with_autoapprove_label(webhook_handler, mock_github_client):
        """Test PR opened with autoapprove label triggers approval."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'error' == 'success'
E         
E         - success
E         + error

tests/test_webhook_handler.py:70: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:50:06.128948Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T13:50:06.129610Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T13:50:06.130169Z [warning  ] Invalid stampbot.toml in octocat/hello-world: None _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=warning extra={'repo': 'octocat/hello-world', 'error': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:50:06.128948Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:50:06.129610Z'}
WARNING  stampbot.webhook_handler:webhook_handler.py:176 {'extra': {'repo': 'octocat/hello-world', 'error': None}, 'event': 'Invalid stampbot.toml in octocat/hello-world: None', 'level': 'warning', 'timestamp': '2026-03-27T13:50:06.130169Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_opened_with_autoapprove_label - AssertionError: assert 'error' == 'success'
  
  - success
  + error
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 163 passed, 3 deselected in 1.07s
operator: core/AddNot, occurrence: 7
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -187,7 +187,7 @@
                     },
                 )
 
-                if action == "opened":
+                if not action == "opened":
                     await run_in_threadpool(
                         github_client.create_pr_review_comment,
                         installation_id,
........................................................................ [ 34%]
........................................................................ [ 68%]
................................................F
=================================== FAILURES ===================================
____________________ test_invalid_repo_config_posts_review _____________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fb4e398f530>
mock_github_client = <MagicMock name='github_client' id='140414890338896'>

    @pytest.mark.asyncio
    async def test_invalid_repo_config_posts_review(webhook_handler, mock_github_client):
        """Test invalid repo config logs and posts a review comment."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
        mock_github_client.get_repo_file.return_value = 'chatops_required_permission = "invalid"'
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
        assert result["status"] == "error"
        assert "invalid" in result["message"].lower()
>       mock_github_client.create_pr_review_comment.assert_called_once()

tests/test_webhook_handler.py:518: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <MagicMock name='github_client.create_pr_review_comment' id='140414890339904'>

    def assert_called_once(self):
        """assert that the mock was called only once.
        """
        if not self.call_count == 1:
            msg = ("Expected '%s' to have been called once. Called %s times.%s"
                   % (self._mock_name or 'mock',
                      self.call_count,
                      self._calls_repr()))
>           raise AssertionError(msg)
E           AssertionError: Expected 'create_pr_review_comment' to have been called once. Called 0 times.

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:964: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:33:49.838212Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T13:33:49.839548Z [warning  ] Invalid stampbot.toml in octocat/hello-world: Invalid chatops_required_permission: invalid. Valid values: none, read, triage, write, maintain, admin _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=warning extra={'repo': 'octocat/hello-world', 'error': 'Invalid chatops_required_permission: invalid. Valid values: none, read, triage, write, maintain, admin'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:33:49.838212Z'}
WARNING  stampbot.webhook_handler:webhook_handler.py:176 {'extra': {'repo': 'octocat/hello-world', 'error': 'Invalid chatops_required_permission: invalid. Valid values: none, read, triage, write, maintain, admin'}, 'event': 'Invalid stampbot.toml in octocat/hello-world: Invalid chatops_required_permission: invalid. Valid values: none, read, triage, write, maintain, admin', 'level': 'warning', 'timestamp': '2026-03-27T13:33:49.839548Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_invalid_repo_config_posts_review - AssertionError: Expected 'create_pr_review_comment' to have been called once. Called 0 times.
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 192 passed, 3 deselected in 1.63s
operator: core/AddNot, occurrence: 8
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -206,7 +206,7 @@
                     "message": "Invalid repository configuration",
                 }
 
-            if action == "opened":
+            if not action == "opened":
                 for label in repo_config.approval_labels:
                     label_exists = await run_in_threadpool(
                         github_client.repo_has_label,
........................................................................ [ 34%]
........................................................................ [ 68%]
.....................F
=================================== FAILURES ===================================
__________________ test_pr_opened_missing_label_logs_warning ___________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f4d441a4830>
mock_github_client = <MagicMock name='github_client' id='139969771678960'>

    @pytest.mark.asyncio
    async def test_pr_opened_missing_label_logs_warning(webhook_handler, mock_github_client):
        """Test missing approval label triggers label check."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
        mock_github_client.repo_has_label.return_value = False
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
        assert result["status"] == "success"
>       assert mock_github_client.repo_has_label.call_count == 2
E       AssertionError: assert 0 == 2
E        +  where 0 = <MagicMock name='github_client.repo_has_label' id='139969771676272'>.call_count
E        +    where <MagicMock name='github_client.repo_has_label' id='139969771676272'> = <MagicMock name='github_client' id='139969771678960'>.repo_has_label

tests/test_webhook_handler.py:101: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:52:14.988721Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T13:52:14.989294Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T13:52:14.989828Z [info     ] PR #42 has approval label: autoapprove _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:52:14.988721Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:52:14.989294Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:278 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'PR #42 has approval label: autoapprove', 'level': 'info', 'timestamp': '2026-03-27T13:52:14.989828Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_opened_missing_label_logs_warning - AssertionError: assert 0 == 2
 +  where 0 = <MagicMock name='github_client.repo_has_label' id='139969771676272'>.call_count
 +    where <MagicMock name='github_client.repo_has_label' id='139969771676272'> = <MagicMock name='github_client' id='139969771678960'>.repo_has_label
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 165 passed, 3 deselected in 1.08s
operator: core/AddNot, occurrence: 9
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -214,7 +214,7 @@
                         repo_full_name,
                         label,
                     )
-                    if label_exists is False:
+                    if not label_exists is False:
                         logger.warning(
                             "Approval label %s not found in %s",
                             label,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/AddNot, occurrence: 10
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -223,7 +223,7 @@
                         )
 
             # Check if we should approve based on labels
-            if repo_config.auto_approve_on_label and action in [
+            if not repo_config.auto_approve_on_label and action in [
                 "opened",
                 "reopened",
                 "labeled",
........................................................................ [ 34%]
........................................................................ [ 68%]
...................F
=================================== FAILURES ===================================
____________________ test_pr_opened_with_autoapprove_label _____________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f25185f8050>
mock_github_client = <MagicMock name='github_client' id='139797297923136'>

    @pytest.mark.asyncio
    async def test_pr_opened_with_autoapprove_label(webhook_handler, mock_github_client):
        """Test PR opened with autoapprove label triggers approval."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'ignored' == 'success'
E         
E         - success
E         + ignored

tests/test_webhook_handler.py:70: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:51:49.632316Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T13:51:49.633002Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:51:49.632316Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:51:49.633002Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_opened_with_autoapprove_label - AssertionError: assert 'ignored' == 'success'
  
  - success
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 163 passed, 3 deselected in 1.07s
operator: core/AddNot, occurrence: 11
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -234,7 +234,7 @@
                 pr_author = pr.get("user", {}).get("login", "")
 
                 for label in labels:
-                    if label in repo_config.approval_labels:
+                    if not label in repo_config.approval_labels:
                         # Check if team membership verification is needed
                         author_team_slugs: list[str] | None = None
                         if repo_config.needs_team_check(pr_author) and owner_login:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................F
=================================== FAILURES ===================================
____________________ test_pr_opened_with_autoapprove_label _____________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f5ef4d04050>
mock_github_client = <MagicMock name='github_client' id='140045811516480'>

    @pytest.mark.asyncio
    async def test_pr_opened_with_autoapprove_label(webhook_handler, mock_github_client):
        """Test PR opened with autoapprove label triggers approval."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'ignored' == 'success'
E         
E         - success
E         + ignored

tests/test_webhook_handler.py:70: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:49:45.127662Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T13:49:45.128336Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:49:45.127662Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:49:45.128336Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_opened_with_autoapprove_label - AssertionError: assert 'ignored' == 'success'
  
  - success
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 163 passed, 3 deselected in 1.07s
operator: core/AddNot, occurrence: 12
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -237,7 +237,7 @@
                     if label in repo_config.approval_labels:
                         # Check if team membership verification is needed
                         author_team_slugs: list[str] | None = None
-                        if repo_config.needs_team_check(pr_author) and owner_login:
+                        if not repo_config.needs_team_check(pr_author) and owner_login:
                             author_team_slugs = await run_in_threadpool(
                                 github_client.get_user_team_slugs,
                                 installation_id,
........................................................................ [ 34%]
........................................................................ [ 68%]
................................................................F
=================================== FAILURES ===================================
________________ test_pr_with_allowed_teams_triggers_team_check ________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fdbebe6f1d0>
mock_github_client = <MagicMock name='github_client' id='140582527172688'>

    @pytest.mark.asyncio
    async def test_pr_with_allowed_teams_triggers_team_check(webhook_handler, mock_github_client):
        """Test PR with allowed_teams configured triggers team membership check."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
        # Config with allowed_teams - user not in allowed_users so needs team check
        mock_github_client.get_repo_file.return_value = """
    allowed_teams = ["acme/release-team"]
    """
        # User is in the allowed team
        mock_github_client.get_user_team_slugs.return_value = ["release-team"]
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'ignored' == 'success'
E         
E         - success
E         + ignored

tests/test_webhook_handler.py:811: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:52:10.860114Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T13:52:10.861379Z [info     ] PR #42 not eligible for auto-approval: PR author not a member of any allowed team _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'pr_author': 'contributor', 'reason': 'PR author not a member of any allowed team'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:52:10.860114Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:254 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'pr_author': 'contributor', 'reason': 'PR author not a member of any allowed team'}, 'event': 'PR #42 not eligible for auto-approval: PR author not a member of any allowed team', 'level': 'info', 'timestamp': '2026-03-27T13:52:10.861379Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_with_allowed_teams_triggers_team_check - AssertionError: assert 'ignored' == 'success'
  
  - success
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 208 passed, 3 deselected in 1.63s
operator: core/AddNot, occurrence: 13
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -250,7 +250,7 @@
                         is_eligible, reason = repo_config.is_pr_eligible(
                             labels, pr_title, pr_author, author_team_slugs
                         )
-                        if not is_eligible:
+                        if not not is_eligible:
                             logger.info(
                                 "PR #%d not eligible for auto-approval: %s",
                                 pr_number,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................F
=================================== FAILURES ===================================
____________________ test_pr_opened_with_autoapprove_label _____________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f395551c050>
mock_github_client = <MagicMock name='github_client' id='139884221858880'>

    @pytest.mark.asyncio
    async def test_pr_opened_with_autoapprove_label(webhook_handler, mock_github_client):
        """Test PR opened with autoapprove label triggers approval."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'ignored' == 'success'
E         
E         - success
E         + ignored

tests/test_webhook_handler.py:70: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:35:34.719320Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T13:35:34.719991Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T13:35:34.720857Z [info     ] PR #42 not eligible for auto-approval: None _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'pr_author': 'contributor', 'reason': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:35:34.719320Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:35:34.719991Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:254 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'pr_author': 'contributor', 'reason': None}, 'event': 'PR #42 not eligible for auto-approval: None', 'level': 'info', 'timestamp': '2026-03-27T13:35:34.720857Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_opened_with_autoapprove_label - AssertionError: assert 'ignored' == 'success'
  
  - success
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 163 passed, 3 deselected in 1.06s
operator: core/AddNot, occurrence: 14
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -298,7 +298,7 @@
                         add_span_attributes(
                             span,
                             {
-                                "webhook.result": "approved" if success else "approval_failed",
+                                "webhook.result": "approved" if not success else "approval_failed",
                                 "webhook.trigger_label": label,
                             },
                         )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/AddNot, occurrence: 15
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -305,7 +305,7 @@
                         set_span_ok(span)
 
                         return {
-                            "status": "success" if success else "error",
+                            "status": "success" if not success else "error",
                             "message": (
                                 f"PR approved via label: {label}"
                                 if success
........................................................................ [ 34%]
........................................................................ [ 68%]
...................F
=================================== FAILURES ===================================
____________________ test_pr_opened_with_autoapprove_label _____________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f9bd01f4050>
mock_github_client = <MagicMock name='github_client' id='140307118703680'>

    @pytest.mark.asyncio
    async def test_pr_opened_with_autoapprove_label(webhook_handler, mock_github_client):
        """Test PR opened with autoapprove label triggers approval."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'error' == 'success'
E         
E         - success
E         + error

tests/test_webhook_handler.py:70: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:54:07.004539Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T13:54:07.005229Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T13:54:07.006089Z [info     ] PR #42 has approval label: autoapprove _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:54:07.004539Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:54:07.005229Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:278 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'PR #42 has approval label: autoapprove', 'level': 'info', 'timestamp': '2026-03-27T13:54:07.006089Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_opened_with_autoapprove_label - AssertionError: assert 'error' == 'success'
  
  - success
  + error
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 163 passed, 3 deselected in 1.07s
operator: core/AddNot, occurrence: 16
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -308,7 +308,7 @@
                             "status": "success" if success else "error",
                             "message": (
                                 f"PR approved via label: {label}"
-                                if success
+                                if not success
                                 else "Failed to approve PR"
                             ),
                         }
........................................................................ [ 34%]
........................................................................ [ 68%]
...................F
=================================== FAILURES ===================================
____________________ test_pr_opened_with_autoapprove_label _____________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f59b9c04050>
mock_github_client = <MagicMock name='github_client' id='140023345775680'>

    @pytest.mark.asyncio
    async def test_pr_opened_with_autoapprove_label(webhook_handler, mock_github_client):
        """Test PR opened with autoapprove label triggers approval."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
        assert result["status"] == "success"
>       assert "approved" in result["message"].lower()
E       AssertionError: assert 'approved' in 'failed to approve pr'
E        +  where 'failed to approve pr' = <built-in method lower of str object at 0x7f59b9a4e770>()
E        +    where <built-in method lower of str object at 0x7f59b9a4e770> = 'Failed to approve PR'.lower

tests/test_webhook_handler.py:71: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:32:06.500851Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T13:32:06.501506Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T13:32:06.502373Z [info     ] PR #42 has approval label: autoapprove _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:32:06.500851Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:32:06.501506Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:278 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'PR #42 has approval label: autoapprove', 'level': 'info', 'timestamp': '2026-03-27T13:32:06.502373Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_opened_with_autoapprove_label - AssertionError: assert 'approved' in 'failed to approve pr'
 +  where 'failed to approve pr' = <built-in method lower of str object at 0x7f59b9a4e770>()
 +    where <built-in method lower of str object at 0x7f59b9a4e770> = 'Failed to approve PR'.lower
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 163 passed, 3 deselected in 1.07s
operator: core/AddNot, occurrence: 17
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -314,7 +314,7 @@
                         }
 
             # Check if we should remove approval when label is removed
-            if repo_config.auto_approve_on_label and action == "unlabeled":
+            if not repo_config.auto_approve_on_label and action == "unlabeled":
                 removed_label = payload.get("label", {}).get("name")
                 if removed_label in repo_config.approval_labels:
                     logger.info(
........................................................................ [ 34%]
........................................................................ [ 68%]
.........................F
=================================== FAILURES ===================================
________________________ test_pr_unlabeled_autoapprove _________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fcf9dae9650>
mock_github_client = <MagicMock name='github_client' id='140529745714480'>

    @pytest.mark.asyncio
    async def test_pr_unlabeled_autoapprove(webhook_handler, mock_github_client):
        """Test removing autoapprove label dismisses approvals."""
        payload = load_fixture("pr_unlabeled_autoapprove")
        mock_github_client.find_bot_reviews.return_value = [111, 222]  # Mock review IDs
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'ignored' == 'success'
E         
E         - success
E         + ignored

tests/test_webhook_handler.py:152: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:44:41.195520Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-03-27T13:44:41.196071Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'unlabeled'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:44:41.195520Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:44:41.196071Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_unlabeled_autoapprove - AssertionError: assert 'ignored' == 'success'
  
  - success
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 169 passed, 3 deselected in 1.09s
operator: core/AddNot, occurrence: 18
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -316,7 +316,7 @@
             # Check if we should remove approval when label is removed
             if repo_config.auto_approve_on_label and action == "unlabeled":
                 removed_label = payload.get("label", {}).get("name")
-                if removed_label in repo_config.approval_labels:
+                if not removed_label in repo_config.approval_labels:
                     logger.info(
                         "Approval label %s removed from PR #%d",
                         removed_label,
........................................................................ [ 34%]
........................................................................ [ 68%]
.........................F
=================================== FAILURES ===================================
________________________ test_pr_unlabeled_autoapprove _________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f3b53bcd450>
mock_github_client = <MagicMock name='github_client' id='139892846812464'>

    @pytest.mark.asyncio
    async def test_pr_unlabeled_autoapprove(webhook_handler, mock_github_client):
        """Test removing autoapprove label dismisses approvals."""
        payload = load_fixture("pr_unlabeled_autoapprove")
        mock_github_client.find_bot_reviews.return_value = [111, 222]  # Mock review IDs
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'ignored' == 'success'
E         
E         - success
E         + ignored

tests/test_webhook_handler.py:152: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:37:51.564882Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-03-27T13:37:51.565412Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'unlabeled'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:37:51.564882Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:37:51.565412Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_unlabeled_autoapprove - AssertionError: assert 'ignored' == 'success'
  
  - success
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 169 passed, 3 deselected in 1.10s
operator: core/AddNot, occurrence: 19
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -340,7 +340,7 @@
                     add_span_attributes(
                         span,
                         {
-                            "webhook.result": "dismissed" if success else "dismiss_failed",
+                            "webhook.result": "dismissed" if not success else "dismiss_failed",
                             "webhook.removed_label": removed_label,
                         },
                     )
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/AddNot, occurrence: 20
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -347,7 +347,7 @@
                     set_span_ok(span)
 
                     return {
-                        "status": "success" if success else "error",
+                        "status": "success" if not success else "error",
                         "message": (
                             "Approvals dismissed" if success else "Failed to dismiss approvals"
                         ),
........................................................................ [ 34%]
........................................................................ [ 68%]
.........................F
=================================== FAILURES ===================================
________________________ test_pr_unlabeled_autoapprove _________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fd58b4ed350>
mock_github_client = <MagicMock name='github_client' id='140555207269680'>

    @pytest.mark.asyncio
    async def test_pr_unlabeled_autoapprove(webhook_handler, mock_github_client):
        """Test removing autoapprove label dismisses approvals."""
        payload = load_fixture("pr_unlabeled_autoapprove")
        mock_github_client.find_bot_reviews.return_value = [111, 222]  # Mock review IDs
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'error' == 'success'
E         
E         - success
E         + error

tests/test_webhook_handler.py:152: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:43:59.711260Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-03-27T13:43:59.711774Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T13:43:59.712314Z [info     ] Approval label autoapprove removed from PR #42 _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'unlabeled'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:43:59.711260Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:43:59.711774Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:320 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'Approval label autoapprove removed from PR #42', 'level': 'info', 'timestamp': '2026-03-27T13:43:59.712314Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_unlabeled_autoapprove - AssertionError: assert 'error' == 'success'
  
  - success
  + error
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 169 passed, 3 deselected in 1.09s
operator: core/AddNot, occurrence: 21
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -349,7 +349,7 @@
                     return {
                         "status": "success" if success else "error",
                         "message": (
-                            "Approvals dismissed" if success else "Failed to dismiss approvals"
+                            "Approvals dismissed" if not success else "Failed to dismiss approvals"
                         ),
                     }
 
........................................................................ [ 34%]
........................................................................ [ 68%]
.........................F
=================================== FAILURES ===================================
________________________ test_pr_unlabeled_autoapprove _________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f50f28e9450>
mock_github_client = <MagicMock name='github_client' id='139985715207472'>

    @pytest.mark.asyncio
    async def test_pr_unlabeled_autoapprove(webhook_handler, mock_github_client):
        """Test removing autoapprove label dismisses approvals."""
        payload = load_fixture("pr_unlabeled_autoapprove")
        mock_github_client.find_bot_reviews.return_value = [111, 222]  # Mock review IDs
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
        assert result["status"] == "success"
>       assert "dismissed" in result["message"].lower()
E       AssertionError: assert 'dismissed' in 'failed to dismiss approvals'
E        +  where 'failed to dismiss approvals' = <built-in method lower of str object at 0x7f50f26c9200>()
E        +    where <built-in method lower of str object at 0x7f50f26c9200> = 'Failed to dismiss approvals'.lower

tests/test_webhook_handler.py:153: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:44:20.960815Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-03-27T13:44:20.961343Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T13:44:20.961886Z [info     ] Approval label autoapprove removed from PR #42 _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'unlabeled'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:44:20.960815Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:44:20.961343Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:320 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'Approval label autoapprove removed from PR #42', 'level': 'info', 'timestamp': '2026-03-27T13:44:20.961886Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_unlabeled_autoapprove - AssertionError: assert 'dismissed' in 'failed to dismiss approvals'
 +  where 'failed to dismiss approvals' = <built-in method lower of str object at 0x7f50f26c9200>()
 +    where <built-in method lower of str object at 0x7f50f26c9200> = 'Failed to dismiss approvals'.lower
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 169 passed, 3 deselected in 1.10s
operator: core/AddNot, occurrence: 22
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -370,7 +370,7 @@
         comment_body = comment.get("body", "")
 
         # Security: limit comment length to prevent DoS
-        if len(comment_body) > MAX_COMMENT_LENGTH:
+        if not len(comment_body) > MAX_COMMENT_LENGTH:
             return {"status": "ignored", "message": "Comment too long"}
 
         comment_body = comment_body.lower().strip()
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f989026e150>
mock_github_client = <MagicMock name='github_client' id='140293230767680'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
        result = await webhook_handler.handle_event("issue_comment", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'ignored' == 'success'
E         
E         - success
E         + ignored

tests/test_webhook_handler.py:259: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:08:05.247089Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T14:08:05.247089Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - AssertionError: assert 'ignored' == 'success'
  
  - success
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.12s
operator: core/AddNot, occurrence: 23
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -376,7 +376,7 @@
         comment_body = comment_body.lower().strip()
 
         # Check if bot is mentioned
-        if "@stampbot" not in comment_body:
+        if not "@stampbot" not in comment_body:
             return {"status": "ignored", "message": "Bot not mentioned"}
 
         # Extract PR info
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f6a7854e150>
mock_github_client = <MagicMock name='github_client' id='140095262633536'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
        result = await webhook_handler.handle_event("issue_comment", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'ignored' == 'success'
E         
E         - success
E         + ignored

tests/test_webhook_handler.py:259: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:56:59.337885Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T13:56:59.337885Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - AssertionError: assert 'ignored' == 'success'
  
  - success
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.13s
operator: core/AddNot, occurrence: 24
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -380,7 +380,7 @@
             return {"status": "ignored", "message": "Bot not mentioned"}
 
         # Extract PR info
-        if "pull_request" in payload.get("issue", {}):
+        if not "pull_request" in payload.get("issue", {}):
             pr_url = payload["issue"]["pull_request"]["url"]
             # Extract PR number from URL
             pr_number = int(pr_url.split("/")[-1])
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f1d14442150>
mock_github_client = <MagicMock name='github_client' id='139762871332416'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
        result = await webhook_handler.handle_event("issue_comment", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'error' == 'success'
E         
E         - success
E         + error

tests/test_webhook_handler.py:259: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:01:27.380325Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T14:01:27.380325Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - AssertionError: assert 'error' == 'success'
  
  - success
  + error
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.12s
operator: core/AddNot, occurrence: 25
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -406,7 +406,7 @@
                 "chatops.commenter": commenter,
             },
         ) as span:
-            if not all([pr_number, repo_full_name, installation_id]):
+            if not not all([pr_number, repo_full_name, installation_id]):
                 logger.warning("Missing required fields in comment event")
                 add_span_attributes(span, {"chatops.result": "missing_fields"})
                 set_span_ok(span)
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f8ffc162150>
mock_github_client = <MagicMock name='github_client' id='140256386903616'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
        result = await webhook_handler.handle_event("issue_comment", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'error' == 'success'
E         
E         - success
E         + error

tests/test_webhook_handler.py:259: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:08:03.007820Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-03-27T14:08:03.008024Z [warning  ] Missing required fields in comment event _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=warning
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T14:08:03.007820Z'}
WARNING  stampbot.webhook_handler:webhook_handler.py:410 {'event': 'Missing required fields in comment event', 'level': 'warning', 'timestamp': '2026-03-27T14:08:03.008024Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - AssertionError: assert 'error' == 'success'
  
  - success
  + error
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.14s
operator: core/AddNot, occurrence: 26
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -421,7 +421,7 @@
                 owner_type,
             )
 
-            if repo_config.config_error:
+            if not repo_config.config_error:
                 add_span_attributes(
                     span,
                     {
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fba009f2780>
mock_github_client = <MagicMock name='github_client' id='140436848750144'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
        result = await webhook_handler.handle_event("issue_comment", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'error' == 'success'
E         
E         - success
E         + error

tests/test_webhook_handler.py:259: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:43:53.168363Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-03-27T13:43:53.168934Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T13:43:53.168363Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:43:53.168934Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - AssertionError: assert 'error' == 'success'
  
  - success
  + error
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.13s
operator: core/AddNot, occurrence: 27
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -435,7 +435,7 @@
                     "message": "Invalid repository configuration",
                 }
 
-            if not repo_config.chatops_enabled:
+            if not not repo_config.chatops_enabled:
                 add_span_attributes(span, {"chatops.result": "disabled"})
                 set_span_ok(span)
                 return {"status": "ignored", "message": "Chatops not enabled"}
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fa2aab4a150>
mock_github_client = <MagicMock name='github_client' id='140336625937984'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
        result = await webhook_handler.handle_event("issue_comment", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'ignored' == 'success'
E         
E         - success
E         + ignored

tests/test_webhook_handler.py:259: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:37:18.986237Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-03-27T13:37:18.986768Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T13:37:18.986237Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:37:18.986768Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - AssertionError: assert 'ignored' == 'success'
  
  - success
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.12s
operator: core/AddNot, occurrence: 28
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -442,7 +442,7 @@
 
             # Parse command
             command_match = re.search(r"@stampbot\s+(\w+)", comment_body)
-            if not command_match:
+            if not not command_match:
                 chatops_commands_total.labels(command="none", status="ignored").inc()
                 add_span_attributes(span, {"chatops.result": "no_command"})
                 set_span_ok(span)
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f17d2462150>
mock_github_client = <MagicMock name='github_client' id='139740289330752'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
        result = await webhook_handler.handle_event("issue_comment", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'ignored' == 'success'
E         
E         - success
E         + ignored

tests/test_webhook_handler.py:259: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:38:08.467412Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-03-27T13:38:08.467985Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T13:38:08.467412Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:38:08.467985Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - AssertionError: assert 'ignored' == 'success'
  
  - success
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.13s
operator: core/AddNot, occurrence: 29
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -451,7 +451,7 @@
             command = command_match.group(1).lower()
             add_span_attributes(span, {"chatops.command": command})
 
-            if command in repo_config.approve_commands + repo_config.unapprove_commands:
+            if not command in repo_config.approve_commands + repo_config.unapprove_commands:
                 has_permission = await run_in_threadpool(
                     github_client.user_has_permission,
                     installation_id,
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f109c172150>
mock_github_client = <MagicMock name='github_client' id='139709315509824'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
        result = await webhook_handler.handle_event("issue_comment", payload)
    
        assert result["status"] == "success"
        assert "approved" in result["message"].lower()
        mock_github_client.approve_pr.assert_called_once()
>       mock_github_client.user_has_permission.assert_called_once()

tests/test_webhook_handler.py:262: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <MagicMock name='github_client.user_has_permission' id='139709315512176'>

    def assert_called_once(self):
        """assert that the mock was called only once.
        """
        if not self.call_count == 1:
            msg = ("Expected '%s' to have been called once. Called %s times.%s"
                   % (self._mock_name or 'mock',
                      self.call_count,
                      self._calls_repr()))
>           raise AssertionError(msg)
E           AssertionError: Expected 'user_has_permission' to have been called once. Called 0 times.

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:964: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:35:20.954654Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-03-27T13:35:20.955209Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T13:35:20.954654Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:35:20.955209Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - AssertionError: Expected 'user_has_permission' to have been called once. Called 0 times.
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.16s
operator: core/AddNot, occurrence: 30
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -459,7 +459,7 @@
                     commenter,
                     repo_config.chatops_required_permission,
                 )
-                if not has_permission:
+                if not not has_permission:
                     chatops_commands_total.labels(command=command, status="forbidden").inc()
                     add_span_attributes(
                         span,
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f7db1c7e150>
mock_github_client = <MagicMock name='github_client' id='140177828769344'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
        result = await webhook_handler.handle_event("issue_comment", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'ignored' == 'success'
E         
E         - success
E         + ignored

tests/test_webhook_handler.py:259: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:39:46.749312Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-03-27T13:39:46.749860Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T13:39:46.749312Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:39:46.749860Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - AssertionError: assert 'ignored' == 'success'
  
  - success
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.12s
operator: core/AddNot, occurrence: 31
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -477,7 +477,7 @@
                     }
 
             # Handle approve commands
-            if command in repo_config.approve_commands:
+            if not command in repo_config.approve_commands:
                 success = await self._approve_pr(
                     installation_id,
                     repo_full_name,
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f517461b800>
mock_github_client = <MagicMock name='github_client' id='139987821391424'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
        result = await webhook_handler.handle_event("issue_comment", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'ignored' == 'success'
E         
E         - success
E         + ignored

tests/test_webhook_handler.py:259: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:39:55.176039Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-03-27T13:39:55.176574Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T13:39:55.176039Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:39:55.176574Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - AssertionError: assert 'ignored' == 'success'
  
  - success
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.12s
operator: core/AddNot, occurrence: 32
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -488,7 +488,7 @@
 
                 chatops_commands_total.labels(
                     command="approve",
-                    status="success" if success else "failure",
+                    status="success" if not success else "failure",
                 ).inc()
 
                 add_span_attributes(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/AddNot, occurrence: 33
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -492,7 +492,7 @@
                 ).inc()
 
                 add_span_attributes(
-                    span, {"chatops.result": "approved" if success else "approval_failed"}
+                    span, {"chatops.result": "approved" if not success else "approval_failed"}
                 )
                 set_span_ok(span)
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/AddNot, occurrence: 34
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -497,7 +497,7 @@
                 set_span_ok(span)
 
                 return {
-                    "status": "success" if success else "error",
+                    "status": "success" if not success else "error",
                     "message": "PR approved" if success else "Failed to approve PR",
                 }
 
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f4700362150>
mock_github_client = <MagicMock name='github_client' id='139942923497024'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
        result = await webhook_handler.handle_event("issue_comment", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'error' == 'success'
E         
E         - success
E         + error

tests/test_webhook_handler.py:259: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:33:11.306524Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-03-27T13:33:11.307088Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T13:33:11.306524Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:33:11.307088Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - AssertionError: assert 'error' == 'success'
  
  - success
  + error
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.13s
operator: core/AddNot, occurrence: 35
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -498,7 +498,7 @@
 
                 return {
                     "status": "success" if success else "error",
-                    "message": "PR approved" if success else "Failed to approve PR",
+                    "message": "PR approved" if not success else "Failed to approve PR",
                 }
 
             # Handle unapprove commands
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fa699b5a150>
mock_github_client = <MagicMock name='github_client' id='140353520660032'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
        result = await webhook_handler.handle_event("issue_comment", payload)
    
        assert result["status"] == "success"
>       assert "approved" in result["message"].lower()
E       AssertionError: assert 'approved' in 'failed to approve pr'
E        +  where 'failed to approve pr' = <built-in method lower of str object at 0x7fa69a841b30>()
E        +    where <built-in method lower of str object at 0x7fa69a841b30> = 'Failed to approve PR'.lower

tests/test_webhook_handler.py:260: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:32:33.102088Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-03-27T13:32:33.102632Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T13:32:33.102088Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:32:33.102632Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - AssertionError: assert 'approved' in 'failed to approve pr'
 +  where 'failed to approve pr' = <built-in method lower of str object at 0x7fa69a841b30>()
 +    where <built-in method lower of str object at 0x7fa69a841b30> = 'Failed to approve PR'.lower
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.12s
operator: core/AddNot, occurrence: 36
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -513,7 +513,7 @@
 
                 chatops_commands_total.labels(
                     command="unapprove",
-                    status="success" if success else "failure",
+                    status="success" if not success else "failure",
                 ).inc()
 
                 add_span_attributes(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/AddNot, occurrence: 37
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -517,7 +517,7 @@
                 ).inc()
 
                 add_span_attributes(
-                    span, {"chatops.result": "unapproved" if success else "unapprove_failed"}
+                    span, {"chatops.result": "unapproved" if not success else "unapprove_failed"}
                 )
                 set_span_ok(span)
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/AddNot, occurrence: 38
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -522,7 +522,7 @@
                 set_span_ok(span)
 
                 return {
-                    "status": "success" if success else "error",
+                    "status": "success" if not success else "error",
                     "message": "Approvals dismissed" if success else "Failed to dismiss approvals",
                 }
 
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................F
=================================== FAILURES ===================================
_________________________ test_issue_comment_unapprove _________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f2d2af0f750>
mock_github_client = <MagicMock name='github_client' id='139832057267936'>

    @pytest.mark.asyncio
    async def test_issue_comment_unapprove(webhook_handler, mock_github_client):
        """Test @stampbot unapprove command."""
        payload = load_fixture("issue_comment_unapprove")
        mock_github_client.find_bot_reviews.return_value = [111]
    
        result = await webhook_handler.handle_event("issue_comment", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'error' == 'success'
E         
E         - success
E         + error

tests/test_webhook_handler.py:289: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:34:09.473950Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-03-27T13:34:09.474497Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T13:34:09.473950Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:34:09.474497Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_unapprove - AssertionError: assert 'error' == 'success'
  
  - success
  + error
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 179 passed, 3 deselected in 1.16s
operator: core/AddNot, occurrence: 39
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -523,7 +523,7 @@
 
                 return {
                     "status": "success" if success else "error",
-                    "message": "Approvals dismissed" if success else "Failed to dismiss approvals",
+                    "message": "Approvals dismissed" if not success else "Failed to dismiss approvals",
                 }
 
             chatops_commands_total.labels(command="unknown", status="ignored").inc()
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................F
=================================== FAILURES ===================================
_________________________ test_issue_comment_unapprove _________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7efefd647750>
mock_github_client = <MagicMock name='github_client' id='139633656317664'>

    @pytest.mark.asyncio
    async def test_issue_comment_unapprove(webhook_handler, mock_github_client):
        """Test @stampbot unapprove command."""
        payload = load_fixture("issue_comment_unapprove")
        mock_github_client.find_bot_reviews.return_value = [111]
    
        result = await webhook_handler.handle_event("issue_comment", payload)
    
        assert result["status"] == "success"
>       assert "dismissed" in result["message"].lower()
E       AssertionError: assert 'dismissed' in 'failed to dismiss approvals'
E        +  where 'failed to dismiss approvals' = <built-in method lower of str object at 0x7efefe4e51b0>()
E        +    where <built-in method lower of str object at 0x7efefe4e51b0> = 'Failed to dismiss approvals'.lower

tests/test_webhook_handler.py:290: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:32:17.388677Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-03-27T13:32:17.389225Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T13:32:17.388677Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:32:17.389225Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_unapprove - AssertionError: assert 'dismissed' in 'failed to dismiss approvals'
 +  where 'failed to dismiss approvals' = <built-in method lower of str object at 0x7efefe4e51b0>()
 +    where <built-in method lower of str object at 0x7efefe4e51b0> = 'Failed to dismiss approvals'.lower
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 179 passed, 3 deselected in 1.15s
operator: core/AddNot, occurrence: 40
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -567,7 +567,7 @@
                     default_branch,
                 )
 
-                if content:
+                if not content:
                     return self._parse_repo_config(span, content, source_repo=repo_full_name)
 
                 org_repo_full_name = None
........................................................................ [ 34%]
........................................................................ [ 68%]
......................F
=================================== FAILURES ===================================
_______________ test_pr_label_ignored_when_auto_approve_disabled _______________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f284a836470>
mock_github_client = <MagicMock name='github_client' id='139811025673184'>

    @pytest.mark.asyncio
    async def test_pr_label_ignored_when_auto_approve_disabled(webhook_handler, mock_github_client):
        """Test label approvals are ignored when auto_approve_on_label is disabled."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
        mock_github_client.get_repo_file.return_value = "auto_approve_on_label = false"
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "ignored"
E       AssertionError: assert 'success' == 'ignored'
E         
E         - ignored
E         + success

tests/test_webhook_handler.py:112: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:36:13.111132Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T13:36:13.111668Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T13:36:13.112469Z [info     ] PR #42 has approval label: autoapprove _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:36:13.111132Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:36:13.111668Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:278 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'PR #42 has approval label: autoapprove', 'level': 'info', 'timestamp': '2026-03-27T13:36:13.112469Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_label_ignored_when_auto_approve_disabled - AssertionError: assert 'success' == 'ignored'
  
  - ignored
  + success
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 166 passed, 3 deselected in 1.07s
operator: core/AddNot, occurrence: 41
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -571,7 +571,7 @@
                     return self._parse_repo_config(span, content, source_repo=repo_full_name)
 
                 org_repo_full_name = None
-                if (
+                if not (
                     owner_type == "Organization"
                     and owner_login
                     and repo_full_name != f"{owner_login}/.github"
........................................................................ [ 34%]
........................................................................ [ 68%]
............................................F
=================================== FAILURES ===================================
_____________________ test_repo_config_uses_default_branch _____________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f8f17777530>
mock_github_client = <MagicMock name='github_client' id='140252550100512'>

    @pytest.mark.asyncio
    async def test_repo_config_uses_default_branch(webhook_handler, mock_github_client):
        """Test repo config is loaded from the default branch."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
        payload["repository"]["default_branch"] = "develop"
        payload["pull_request"]["base"]["ref"] = "release"
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
        assert result["status"] == "success"
        args = mock_github_client.get_repo_file.call_args[0]
>       assert args[3] == "develop"
E       AssertionError: assert None == 'develop'

tests/test_webhook_handler.py:434: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:49:17.527824Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T13:49:17.528494Z [info     ] No stampbot.toml found in octocat/hello-world or octocat/.github, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': 'octocat/.github'}
2026-03-27T13:49:17.529315Z [info     ] PR #42 has approval label: autoapprove _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:49:17.527824Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': 'octocat/.github'}, 'event': 'No stampbot.toml found in octocat/hello-world or octocat/.github, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:49:17.528494Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:278 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'PR #42 has approval label: autoapprove', 'level': 'info', 'timestamp': '2026-03-27T13:49:17.529315Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_repo_config_uses_default_branch - AssertionError: assert None == 'develop'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 188 passed, 3 deselected in 1.57s
operator: core/AddNot, occurrence: 42
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -584,7 +584,7 @@
                         "stampbot.toml",
                         None,
                     )
-                    if org_content:
+                    if not org_content:
                         return self._parse_repo_config(
                             span, org_content, source_repo=org_repo_full_name
                         )
........................................................................ [ 34%]
........................................................................ [ 68%]
.............................................F
=================================== FAILURES ===================================
_____________________ test_org_github_repo_config_fallback _____________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f36cea73230>
mock_github_client = <MagicMock name='github_client' id='139873372816112'>

    @pytest.mark.asyncio
    async def test_org_github_repo_config_fallback(webhook_handler, mock_github_client):
        """Test org .github stampbot.toml is used when repo config is missing."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
        payload["repository"]["full_name"] = "acme/widgets"
        payload["repository"]["default_branch"] = "main"
        payload["repository"]["owner"] = {"login": "acme", "type": "Organization"}
        payload["pull_request"]["labels"] = [{"name": "org-approve"}]
    
        def get_repo_file_side_effect(_installation_id, repo_full_name, _file_path, _ref):
            if repo_full_name == "acme/widgets":
                return None
            if repo_full_name == "acme/.github":
                return 'approval_labels = ["org-approve"]'
            return None
    
        mock_github_client.get_repo_file.side_effect = get_repo_file_side_effect
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'ignored' == 'success'
E         
E         - success
E         + ignored

tests/test_webhook_handler.py:457: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:31:17.613618Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T13:31:17.614298Z [info     ] No stampbot.toml found in acme/widgets or acme/.github, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'acme/widgets', 'org_repo': 'acme/.github'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:31:17.613618Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'acme/widgets', 'org_repo': 'acme/.github'}, 'event': 'No stampbot.toml found in acme/widgets or acme/.github, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:31:17.614298Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_org_github_repo_config_fallback - AssertionError: assert 'ignored' == 'success'
  
  - success
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 189 passed, 3 deselected in 1.56s
operator: core/AddNot, occurrence: 43
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -593,7 +593,7 @@
                 logger.info(
                     "No stampbot.toml found in %s%s, using defaults",
                     repo_full_name,
-                    f" or {org_repo_full_name}" if org_repo_full_name else "",
+                    f" or {org_repo_full_name}" if not org_repo_full_name else "",
                     extra={"repo": repo_full_name, "org_repo": org_repo_full_name},
                 )
                 add_span_attributes(span, {"config.result": "default"})
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/AddNot, occurrence: 44
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -689,7 +689,7 @@
                 pr_number,
             )
 
-            if existing_approvals:
+            if not existing_approvals:
                 logger.info(
                     "PR #%d in %s already has active approval, skipping",
                     pr_number,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................F
=================================== FAILURES ===================================
____________________ test_pr_opened_with_autoapprove_label _____________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f66a7e00050>
mock_github_client = <MagicMock name='github_client' id='140078880441408'>

    @pytest.mark.asyncio
    async def test_pr_opened_with_autoapprove_label(webhook_handler, mock_github_client):
        """Test PR opened with autoapprove label triggers approval."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
        assert result["status"] == "success"
        assert "approved" in result["message"].lower()
>       mock_github_client.approve_pr.assert_called_once_with(
            12345,  # installation_id
            "octocat/hello-world",  # repo
            42,  # pr_number
            "Auto-approved by Stampbot (label: autoapprove)",
        )

tests/test_webhook_handler.py:72: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <MagicMock name='github_client.approve_pr' id='140078880440400'>
args = (12345, 'octocat/hello-world', 42, 'Auto-approved by Stampbot (label: autoapprove)')
kwargs = {}, msg = "Expected 'approve_pr' to be called once. Called 0 times."

    def assert_called_once_with(self, /, *args, **kwargs):
        """assert that the mock was called exactly once and that that call was
        with the specified arguments."""
        if not self.call_count == 1:
            msg = ("Expected '%s' to be called once. Called %s times.%s"
                   % (self._mock_name or 'mock',
                      self.call_count,
                      self._calls_repr()))
>           raise AssertionError(msg)
E           AssertionError: Expected 'approve_pr' to be called once. Called 0 times.

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:996: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:59:03.160419Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T13:59:03.161086Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T13:59:03.161953Z [info     ] PR #42 has approval label: autoapprove _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
2026-03-27T13:59:03.162278Z [info     ] PR #42 in octocat/hello-world already has active approval, skipping _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'existing_review_ids': []}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:59:03.160419Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:59:03.161086Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:278 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'PR #42 has approval label: autoapprove', 'level': 'info', 'timestamp': '2026-03-27T13:59:03.161953Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:693 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'existing_review_ids': []}, 'event': 'PR #42 in octocat/hello-world already has active approval, skipping', 'level': 'info', 'timestamp': '2026-03-27T13:59:03.162278Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_opened_with_autoapprove_label - AssertionError: Expected 'approve_pr' to be called once. Called 0 times.
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 163 passed, 3 deselected in 1.11s
operator: core/AddNot, occurrence: 45
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -723,7 +723,7 @@
             duration = time.time() - start_time
             pr_approval_duration_seconds.observe(duration)
 
-            status = "success" if success else "failure"
+            status = "success" if not success else "failure"
             pr_approvals_total.labels(
                 trigger_type=trigger_type,
                 status=status,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/AddNot, occurrence: 46
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -729,7 +729,7 @@
                 status=status,
             ).inc()
 
-            if not success:
+            if not not success:
                 errors_total.labels(error_type="approval_failed").inc()
 
             add_span_attributes(span, {"approval.result": status})
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/AddNot, occurrence: 47
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -778,7 +778,7 @@
 
                 add_span_attributes(span, {"dismissal.reviews_found": len(review_ids)})
 
-                if not review_ids:
+                if not not review_ids:
                     logger.info(
                         "No bot approvals found on PR #%d",
                         pr_number,
........................................................................ [ 34%]
........................................................................ [ 68%]
.........................F
=================================== FAILURES ===================================
________________________ test_pr_unlabeled_autoapprove _________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fb4351f1650>
mock_github_client = <MagicMock name='github_client' id='140412024200496'>

    @pytest.mark.asyncio
    async def test_pr_unlabeled_autoapprove(webhook_handler, mock_github_client):
        """Test removing autoapprove label dismisses approvals."""
        payload = load_fixture("pr_unlabeled_autoapprove")
        mock_github_client.find_bot_reviews.return_value = [111, 222]  # Mock review IDs
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
        assert result["status"] == "success"
        assert "dismissed" in result["message"].lower()
        mock_github_client.find_bot_reviews.assert_called_once()
>       assert mock_github_client.dismiss_approval.call_count == 2
E       AssertionError: assert 0 == 2
E        +  where 0 = <MagicMock name='github_client.dismiss_approval' id='140412024201840'>.call_count
E        +    where <MagicMock name='github_client.dismiss_approval' id='140412024201840'> = <MagicMock name='github_client' id='140412024200496'>.dismiss_approval

tests/test_webhook_handler.py:155: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:29:36.091681Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-03-27T13:29:36.092246Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T13:29:36.092764Z [info     ] Approval label autoapprove removed from PR #42 _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
2026-03-27T13:29:36.093197Z [info     ] No bot approvals found on PR #42 _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'unlabeled'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:29:36.091681Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:29:36.092246Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:320 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'Approval label autoapprove removed from PR #42', 'level': 'info', 'timestamp': '2026-03-27T13:29:36.092764Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:782 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42}, 'event': 'No bot approvals found on PR #42', 'level': 'info', 'timestamp': '2026-03-27T13:29:36.093197Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_unlabeled_autoapprove - AssertionError: assert 0 == 2
 +  where 0 = <MagicMock name='github_client.dismiss_approval' id='140412024201840'>.call_count
 +    where <MagicMock name='github_client.dismiss_approval' id='140412024201840'> = <MagicMock name='github_client' id='140412024200496'>.dismiss_approval
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 169 passed, 3 deselected in 1.11s
operator: core/AddNot, occurrence: 48
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -807,7 +807,7 @@
                 duration = time.time() - start_time
                 pr_dismissal_duration_seconds.observe(duration)
 
-                status = "success" if success else "failure"
+                status = "success" if not success else "failure"
                 pr_dismissals_total.labels(trigger_type=trigger_type, status=status).inc()
 
                 add_span_attributes(span, {"dismissal.result": status})
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceTrueWithFalse, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -708,7 +708,7 @@
                     },
                 )
                 set_span_ok(span)
-                return True
+                return False
 
             start_time = time.time()
 
........................................................................ [ 34%]
........................................................................ [ 68%]
........................F
=================================== FAILURES ===================================
___________________ test_pr_labeled_skips_duplicate_approval ___________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fca19ae5350>
mock_github_client = <MagicMock name='github_client' id='140506055879248'>

    @pytest.mark.asyncio
    async def test_pr_labeled_skips_duplicate_approval(webhook_handler, mock_github_client):
        """Test that existing approval prevents duplicate approval comment."""
        payload = load_fixture("pr_labeled_autoapprove")
        # Simulate existing active approval
        mock_github_client.find_bot_reviews.return_value = [12345]
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'error' == 'success'
E         
E         - success
E         + error

tests/test_webhook_handler.py:137: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:26:51.237987Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'labeled'}
2026-03-27T13:26:51.238535Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T13:26:51.239098Z [info     ] PR #42 has approval label: autoapprove _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
2026-03-27T13:26:51.239423Z [info     ] PR #42 in octocat/hello-world already has active approval, skipping _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'existing_review_ids': [12345]}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'labeled'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:26:51.237987Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:26:51.238535Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:278 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'PR #42 has approval label: autoapprove', 'level': 'info', 'timestamp': '2026-03-27T13:26:51.239098Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:693 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'existing_review_ids': [12345]}, 'event': 'PR #42 in octocat/hello-world already has active approval, skipping', 'level': 'info', 'timestamp': '2026-03-27T13:26:51.239423Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_labeled_skips_duplicate_approval - AssertionError: assert 'error' == 'success'
  
  - success
  + error
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 168 passed, 3 deselected in 1.12s
operator: core/ReplaceTrueWithFalse, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -789,7 +789,7 @@
                     pr_dismissals_total.labels(trigger_type=trigger_type, status="success").inc()
                     add_span_attributes(span, {"dismissal.result": "no_reviews"})
                     set_span_ok(span)
-                    return True
+                    return False
 
                 # Dismiss each review
                 success = True
........................................................................ [ 34%]
........................................................................ [ 68%]
...........................F
=================================== FAILURES ===================================
_______________________ test_pr_unlabeled_no_bot_reviews _______________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fa682fa15e0>
mock_github_client = <MagicMock name='github_client' id='140353134324304'>

    @pytest.mark.asyncio
    async def test_pr_unlabeled_no_bot_reviews(webhook_handler, mock_github_client):
        """Test removing label when no bot reviews exist."""
        payload = load_fixture("pr_unlabeled_autoapprove")
        mock_github_client.find_bot_reviews.return_value = []  # No reviews to dismiss
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'error' == 'success'
E         
E         - success
E         + error

tests/test_webhook_handler.py:179: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:54:53.631878Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-03-27T13:54:53.632409Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T13:54:53.632944Z [info     ] Approval label autoapprove removed from PR #42 _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
2026-03-27T13:54:53.633366Z [info     ] No bot approvals found on PR #42 _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'unlabeled'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:54:53.631878Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:54:53.632409Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:320 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'Approval label autoapprove removed from PR #42', 'level': 'info', 'timestamp': '2026-03-27T13:54:53.632944Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:782 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42}, 'event': 'No bot approvals found on PR #42', 'level': 'info', 'timestamp': '2026-03-27T13:54:53.633366Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_unlabeled_no_bot_reviews - AssertionError: assert 'error' == 'success'
  
  - success
  + error
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 171 passed, 3 deselected in 1.10s
operator: core/ReplaceTrueWithFalse, occurrence: 2
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -792,7 +792,7 @@
                     return True
 
                 # Dismiss each review
-                success = True
+                success = False
                 for review_id in review_ids:
                     result = await run_in_threadpool(
                         github_client.dismiss_approval,
........................................................................ [ 34%]
........................................................................ [ 68%]
.........................F
=================================== FAILURES ===================================
________________________ test_pr_unlabeled_autoapprove _________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fba617f9550>
mock_github_client = <MagicMock name='github_client' id='140438538476848'>

    @pytest.mark.asyncio
    async def test_pr_unlabeled_autoapprove(webhook_handler, mock_github_client):
        """Test removing autoapprove label dismisses approvals."""
        payload = load_fixture("pr_unlabeled_autoapprove")
        mock_github_client.find_bot_reviews.return_value = [111, 222]  # Mock review IDs
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'error' == 'success'
E         
E         - success
E         + error

tests/test_webhook_handler.py:152: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:10:46.810703Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-03-27T14:10:46.811256Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T14:10:46.811770Z [info     ] Approval label autoapprove removed from PR #42 _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'unlabeled'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T14:10:46.810703Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T14:10:46.811256Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:320 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'Approval label autoapprove removed from PR #42', 'level': 'info', 'timestamp': '2026-03-27T14:10:46.811770Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_unlabeled_autoapprove - AssertionError: assert 'error' == 'success'
  
  - success
  + error
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 169 passed, 3 deselected in 1.10s
operator: core/ReplaceFalseWithTrue, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -68,7 +68,7 @@
             True if signature is valid
         """
         if not signature:
-            return False
+            return True
 
         expected_signature = (
             "sha256=" + hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
........................................................................ [ 34%]
.........................F
=================================== FAILURES ===================================
________________________ test_webhook_missing_signature ________________________

test_client = <starlette.testclient.TestClient object at 0x7f1a637d4490>

    def test_webhook_missing_signature(test_client: TestClient):
        """Test webhook rejects requests without signature."""
        response = test_client.post(
            "/webhook",
            json={"zen": "test"},
            headers={"X-GitHub-Event": "ping"},
        )
>       assert response.status_code == 401
E       assert 200 == 401
E        +  where 200 = <Response [200 OK]>.status_code

tests/test_main.py:89: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:39:29.056474Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-03-27T13:39:29.057398Z [info     ] HTTP Request: POST http://testserver/webhook "HTTP/1.1 200 OK"
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'ping', 'action': ''}, 'event': 'Received ping event', 'client_ip': 'testclient', 'level': 'info', 'timestamp': '2026-03-27T13:39:29.056474Z'}
INFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/webhook "HTTP/1.1 200 OK"
=========================== short test summary info ============================
FAILED tests/test_main.py::test_webhook_missing_signature - assert 200 == 401
 +  where 200 = <Response [200 OK]>.status_code
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 97 passed, 3 deselected in 0.85s
operator: core/ReplaceFalseWithTrue, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -214,7 +214,7 @@
                         repo_full_name,
                         label,
                     )
-                    if label_exists is False:
+                    if label_exists is True:
                         logger.warning(
                             "Approval label %s not found in %s",
                             label,
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceFalseWithTrue, occurrence: 2
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -832,7 +832,7 @@
                 errors_total.labels(error_type="dismiss_failed").inc()
 
                 set_span_error(span, e)
-                return False
+                return True
 
 
 # Global handler instance
........................................................................ [ 34%]
........................................................................ [ 68%]
............................................................F
=================================== FAILURES ===================================
_______________________ test_dismiss_approvals_exception _______________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f4e33073ef0>
mock_github_client = <MagicMock name='github_client' id='139973834756112'>

    @pytest.mark.asyncio
    async def test_dismiss_approvals_exception(webhook_handler, mock_github_client):
        """Test _dismiss_approvals handles exceptions gracefully."""
        payload = load_fixture("pr_unlabeled_autoapprove")
    
        # Make find_bot_reviews raise an exception
        mock_github_client.find_bot_reviews.side_effect = Exception("API error")
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "error"
E       AssertionError: assert 'success' == 'error'
E         
E         - error
E         + success

tests/test_webhook_handler.py:729: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:49:27.351439Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-03-27T13:49:27.352010Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T13:49:27.352548Z [info     ] Approval label autoapprove removed from PR #42 _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
2026-03-27T13:49:27.352950Z [error    ] Error dismissing approvals: API error _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=error extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'error': 'API error'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'unlabeled'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:49:27.351439Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:49:27.352010Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:320 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'Approval label autoapprove removed from PR #42', 'level': 'info', 'timestamp': '2026-03-27T13:49:27.352548Z'}
ERROR    stampbot.webhook_handler:webhook_handler.py:823 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'error': 'API error'}, 'event': 'Error dismissing approvals: API error', 'level': 'error', 'timestamp': '2026-03-27T13:49:27.352950Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_dismiss_approvals_exception - AssertionError: assert 'success' == 'error'
  
  - error
  + success
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 204 passed, 3 deselected in 1.63s
operator: core/ReplaceAndWithOr, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -223,7 +223,7 @@
                         )
 
             # Check if we should approve based on labels
-            if repo_config.auto_approve_on_label and action in [
+            if repo_config.auto_approve_on_label or action in [
                 "opened",
                 "reopened",
                 "labeled",
........................................................................ [ 34%]
........................................................................ [ 68%]
......................F
=================================== FAILURES ===================================
_______________ test_pr_label_ignored_when_auto_approve_disabled _______________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f44ddc2a7a0>
mock_github_client = <MagicMock name='github_client' id='139933755153712'>

    @pytest.mark.asyncio
    async def test_pr_label_ignored_when_auto_approve_disabled(webhook_handler, mock_github_client):
        """Test label approvals are ignored when auto_approve_on_label is disabled."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
        mock_github_client.get_repo_file.return_value = "auto_approve_on_label = false"
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "ignored"
E       AssertionError: assert 'success' == 'ignored'
E         
E         - ignored
E         + success

tests/test_webhook_handler.py:112: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:43:09.896126Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T13:43:09.897399Z [info     ] PR #42 has approval label: autoapprove _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:43:09.896126Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:278 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'PR #42 has approval label: autoapprove', 'level': 'info', 'timestamp': '2026-03-27T13:43:09.897399Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_label_ignored_when_auto_approve_disabled - AssertionError: assert 'success' == 'ignored'
  
  - ignored
  + success
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 166 passed, 3 deselected in 1.08s
operator: core/ReplaceAndWithOr, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -237,7 +237,7 @@
                     if label in repo_config.approval_labels:
                         # Check if team membership verification is needed
                         author_team_slugs: list[str] | None = None
-                        if repo_config.needs_team_check(pr_author) and owner_login:
+                        if repo_config.needs_team_check(pr_author) or owner_login:
                             author_team_slugs = await run_in_threadpool(
                                 github_client.get_user_team_slugs,
                                 installation_id,
........................................................................ [ 34%]
........................................................................ [ 68%]
..................................................................F
=================================== FAILURES ===================================
_________________ test_pr_with_allowed_users_skips_team_check __________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7ff973843170>
mock_github_client = <MagicMock name='github_client' id='140709361965040'>

    @pytest.mark.asyncio
    async def test_pr_with_allowed_users_skips_team_check(webhook_handler, mock_github_client):
        """Test PR with user in allowed_users skips team membership check."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
        # The fixture has user "contributor"
        mock_github_client.get_repo_file.return_value = """
    allowed_users = ["contributor"]
    allowed_teams = ["acme/release-team"]
    """
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
        assert result["status"] == "success"
        # Should NOT call get_user_team_slugs since user is in allowed_users
>       mock_github_client.get_user_team_slugs.assert_not_called()

tests/test_webhook_handler.py:848: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <MagicMock name='github_client.get_user_team_slugs' id='140709361967728'>

    def assert_not_called(self):
        """assert that the mock was never called.
        """
        if self.call_count != 0:
            msg = ("Expected '%s' to not have been called. Called %s times.%s"
                   % (self._mock_name or 'mock',
                      self.call_count,
                      self._calls_repr()))
>           raise AssertionError(msg)
E           AssertionError: Expected 'get_user_team_slugs' to not have been called. Called 1 times.
E           Calls: [call(12345, 'octocat', 'contributor', ['acme/release-team'])].

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:946: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:32:15.076406Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T13:32:15.077816Z [info     ] PR #42 has approval label: autoapprove _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:32:15.076406Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:278 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'PR #42 has approval label: autoapprove', 'level': 'info', 'timestamp': '2026-03-27T13:32:15.077816Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_with_allowed_users_skips_team_check - AssertionError: Expected 'get_user_team_slugs' to not have been called. Called 1 times.
Calls: [call(12345, 'octocat', 'contributor', ['acme/release-team'])].
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 210 passed, 3 deselected in 1.79s
operator: core/ReplaceAndWithOr, occurrence: 2
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -314,7 +314,7 @@
                         }
 
             # Check if we should remove approval when label is removed
-            if repo_config.auto_approve_on_label and action == "unlabeled":
+            if repo_config.auto_approve_on_label or action == "unlabeled":
                 removed_label = payload.get("label", {}).get("name")
                 if removed_label in repo_config.approval_labels:
                     logger.info(
........................................................................ [ 34%]
........................................................................ [ 68%]
..........................F
=================================== FAILURES ===================================
_____________ test_pr_unlabeled_ignored_when_auto_approve_disabled _____________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f4226abd5e0>
mock_github_client = <MagicMock name='github_client' id='139922088964656'>

    @pytest.mark.asyncio
    async def test_pr_unlabeled_ignored_when_auto_approve_disabled(webhook_handler, mock_github_client):
        """Test label removal is ignored when auto_approve_on_label is disabled."""
        payload = load_fixture("pr_unlabeled_autoapprove")
        mock_github_client.get_repo_file.return_value = "auto_approve_on_label = false"
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "ignored"
E       AssertionError: assert 'success' == 'ignored'
E         
E         - ignored
E         + success

tests/test_webhook_handler.py:166: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:06:18.024095Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-03-27T14:06:18.025100Z [info     ] Approval label autoapprove removed from PR #42 _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
2026-03-27T14:06:18.025544Z [info     ] No bot approvals found on PR #42 _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'unlabeled'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T14:06:18.024095Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:320 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'Approval label autoapprove removed from PR #42', 'level': 'info', 'timestamp': '2026-03-27T14:06:18.025100Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:782 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42}, 'event': 'No bot approvals found on PR #42', 'level': 'info', 'timestamp': '2026-03-27T14:06:18.025544Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_unlabeled_ignored_when_auto_approve_disabled - AssertionError: assert 'success' == 'ignored'
  
  - ignored
  + success
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 170 passed, 3 deselected in 1.11s
operator: core/ReplaceAndWithOr, occurrence: 3
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -573,7 +573,7 @@
                 org_repo_full_name = None
                 if (
                     owner_type == "Organization"
-                    and owner_login
+                    or owner_login
                     and repo_full_name != f"{owner_login}/.github"
                 ):
                     org_repo_full_name = f"{owner_login}/.github"
........................................................................ [ 34%]
........................................................................ [ 68%]
............................................F
=================================== FAILURES ===================================
_____________________ test_repo_config_uses_default_branch _____________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f52ff36b590>
mock_github_client = <MagicMock name='github_client' id='139994448229920'>

    @pytest.mark.asyncio
    async def test_repo_config_uses_default_branch(webhook_handler, mock_github_client):
        """Test repo config is loaded from the default branch."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
        payload["repository"]["default_branch"] = "develop"
        payload["pull_request"]["base"]["ref"] = "release"
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
        assert result["status"] == "success"
        args = mock_github_client.get_repo_file.call_args[0]
>       assert args[3] == "develop"
E       AssertionError: assert None == 'develop'

tests/test_webhook_handler.py:434: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:35:59.321575Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T13:35:59.322283Z [info     ] No stampbot.toml found in octocat/hello-world or octocat/.github, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': 'octocat/.github'}
2026-03-27T13:35:59.323105Z [info     ] PR #42 has approval label: autoapprove _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:35:59.321575Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': 'octocat/.github'}, 'event': 'No stampbot.toml found in octocat/hello-world or octocat/.github, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:35:59.322283Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:278 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'PR #42 has approval label: autoapprove', 'level': 'info', 'timestamp': '2026-03-27T13:35:59.323105Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_repo_config_uses_default_branch - AssertionError: assert None == 'develop'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 188 passed, 3 deselected in 1.56s
operator: core/ReplaceAndWithOr, occurrence: 4
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -574,7 +574,7 @@
                 if (
                     owner_type == "Organization"
                     and owner_login
-                    and repo_full_name != f"{owner_login}/.github"
+                    or repo_full_name != f"{owner_login}/.github"
                 ):
                     org_repo_full_name = f"{owner_login}/.github"
                     org_content = await run_in_threadpool(
........................................................................ [ 34%]
........................................................................ [ 68%]
............................................F
=================================== FAILURES ===================================
_____________________ test_repo_config_uses_default_branch _____________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f9493da3ad0>
mock_github_client = <MagicMock name='github_client' id='140276111603232'>

    @pytest.mark.asyncio
    async def test_repo_config_uses_default_branch(webhook_handler, mock_github_client):
        """Test repo config is loaded from the default branch."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
        payload["repository"]["default_branch"] = "develop"
        payload["pull_request"]["base"]["ref"] = "release"
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
        assert result["status"] == "success"
        args = mock_github_client.get_repo_file.call_args[0]
>       assert args[3] == "develop"
E       AssertionError: assert None == 'develop'

tests/test_webhook_handler.py:434: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:27:29.256518Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T13:27:29.257198Z [info     ] No stampbot.toml found in octocat/hello-world or octocat/.github, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': 'octocat/.github'}
2026-03-27T13:27:29.257988Z [info     ] PR #42 has approval label: autoapprove _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:27:29.256518Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': 'octocat/.github'}, 'event': 'No stampbot.toml found in octocat/hello-world or octocat/.github, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:27:29.257198Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:278 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'PR #42 has approval label: autoapprove', 'level': 'info', 'timestamp': '2026-03-27T13:27:29.257988Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_repo_config_uses_default_branch - AssertionError: assert None == 'develop'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 188 passed, 3 deselected in 1.55s
operator: core/ReplaceAndWithOr, occurrence: 5
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -802,7 +802,7 @@
                         review_id,
                         message,
                     )
-                    success = success and result
+                    success = success or result
 
                 duration = time.time() - start_time
                 pr_dismissal_duration_seconds.observe(duration)
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceOrWithAnd, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -143,7 +143,7 @@
         pr_number = pr.get("number")
         repo = payload.get("repository", {})
         repo_full_name = repo.get("full_name")
-        repo_default_branch = repo.get("default_branch") or "main"
+        repo_default_branch = repo.get("default_branch") and "main"
         repo_owner = repo.get("owner", {})
         owner_login = repo_owner.get("login")
         owner_type = repo_owner.get("type")
........................................................................ [ 34%]
........................................................................ [ 68%]
............................................F
=================================== FAILURES ===================================
_____________________ test_repo_config_uses_default_branch _____________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fbb83d974d0>
mock_github_client = <MagicMock name='github_client' id='140443346482720'>

    @pytest.mark.asyncio
    async def test_repo_config_uses_default_branch(webhook_handler, mock_github_client):
        """Test repo config is loaded from the default branch."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
        payload["repository"]["default_branch"] = "develop"
        payload["pull_request"]["base"]["ref"] = "release"
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
        assert result["status"] == "success"
        args = mock_github_client.get_repo_file.call_args[0]
>       assert args[3] == "develop"
E       AssertionError: assert 'main' == 'develop'
E         
E         - develop
E         + main

tests/test_webhook_handler.py:434: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:49:56.167936Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T13:49:56.168469Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T13:49:56.169271Z [info     ] PR #42 has approval label: autoapprove _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:49:56.167936Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:49:56.168469Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:278 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'PR #42 has approval label: autoapprove', 'level': 'info', 'timestamp': '2026-03-27T13:49:56.169271Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_repo_config_uses_default_branch - AssertionError: assert 'main' == 'develop'
  
  - develop
  + main
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 188 passed, 3 deselected in 1.57s
operator: core/ReplaceOrWithAnd, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -152,7 +152,7 @@
         with create_span(
             "webhook.handle_pull_request",
             {
-                "github.repo": repo_full_name or "unknown",
+                "github.repo": repo_full_name and "unknown",
                 "github.pr_number": pr_number or 0,
                 "github.action": action or "unknown",
             },
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.60s
operator: core/ReplaceOrWithAnd, occurrence: 2
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -153,7 +153,7 @@
             "webhook.handle_pull_request",
             {
                 "github.repo": repo_full_name or "unknown",
-                "github.pr_number": pr_number or 0,
+                "github.pr_number": pr_number and 0,
                 "github.action": action or "unknown",
             },
         ) as span:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/ReplaceOrWithAnd, occurrence: 3
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -154,7 +154,7 @@
             {
                 "github.repo": repo_full_name or "unknown",
                 "github.pr_number": pr_number or 0,
-                "github.action": action or "unknown",
+                "github.action": action and "unknown",
             },
         ) as span:
             if not all([pr_number, repo_full_name, installation_id]):
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceOrWithAnd, occurrence: 4
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -391,7 +391,7 @@
 
         repo = payload.get("repository", {})
         repo_full_name = repo.get("full_name")
-        repo_default_branch = repo.get("default_branch") or "main"
+        repo_default_branch = repo.get("default_branch") and "main"
         repo_owner = repo.get("owner", {})
         owner_login = repo_owner.get("login")
         owner_type = repo_owner.get("type")
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceOrWithAnd, occurrence: 5
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -401,7 +401,7 @@
         with create_span(
             "webhook.handle_chatops",
             {
-                "github.repo": repo_full_name or "unknown",
+                "github.repo": repo_full_name and "unknown",
                 "github.pr_number": pr_number,
                 "chatops.commenter": commenter,
             },
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/ReplaceOrWithAnd, occurrence: 6
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -556,7 +556,7 @@
         """
         with create_span(
             "webhook.get_repo_config",
-            {"github.repo": repo_full_name, "github.ref": default_branch or "default"},
+            {"github.repo": repo_full_name, "github.ref": default_branch and "default"},
         ) as span:
             try:
                 content = await run_in_threadpool(
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.68s
operator: core/ExceptionReplacer, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -600,7 +600,7 @@
                 set_span_ok(span)
                 return RepoConfig.default()
 
-            except Exception as e:
+            except CosmicRayTestingException as e:
                 repo_config_loads_total.labels(status="error").inc()
                 logger.warning(
                     "Error loading config from %s: %s, using defaults",
........................................................................ [ 34%]
........................................................................ [ 68%]
...........................................................F
=================================== FAILURES ===================================
________________________ test_get_repo_config_exception ________________________

self = <stampbot.webhook_handler.WebhookHandler object at 0x7f0f075f6030>
installation_id = 12345, repo_full_name = 'octocat/hello-world'
default_branch = 'main', owner_login = 'octocat', owner_type = 'User'

    async def _get_repo_config(
        self,
        installation_id: int,
        repo_full_name: str,
        default_branch: str,
        owner_login: str | None,
        owner_type: str | None,
    ) -> RepoConfig:
        """Get repository configuration from stampbot.toml.
    
        Reads from the repo's default branch first, then falls back to the
        organization-wide .github repo if available.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name
            default_branch: Repository default branch
            owner_login: Repository owner login
            owner_type: Repository owner type (Organization/User)
    
        Returns:
            RepoConfig instance (default if file not found)
        """
        with create_span(
            "webhook.get_repo_config",
            {"github.repo": repo_full_name, "github.ref": default_branch or "default"},
        ) as span:
            try:
>               content = await run_in_threadpool(
                    github_client.get_repo_file,
                    installation_id,
                    repo_full_name,
                    "stampbot.toml",
                    default_branch,
                )

stampbot/webhook_handler.py:562: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/concurrency.py:32: in run_in_threadpool
    return await anyio.to_thread.run_sync(func)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/to_thread.py:63: in run_sync
    return await get_async_backend().run_sync_in_worker_thread(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/_backends/_asyncio.py:2502: in run_sync_in_worker_thread
    return await future
           ^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/_backends/_asyncio.py:986: in run
    result = context.run(func, *args)
             ^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <MagicMock name='github_client.get_repo_file' id='139702517794192'>
args = (12345, 'octocat/hello-world', 'stampbot.toml', 'main'), kwargs = {}
effect = Exception('Network error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: Network error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f0f075f6030>
mock_github_client = <MagicMock name='github_client' id='139702517793856'>

    @pytest.mark.asyncio
    async def test_get_repo_config_exception(webhook_handler, mock_github_client):
        """Test _get_repo_config handles exceptions gracefully."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
    
        # Make get_repo_file raise an exception
        mock_github_client.get_repo_file.side_effect = Exception("Network error")
    
        # Should still work, using defaults
>       result = await webhook_handler.handle_event("pull_request", payload)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_webhook_handler.py:713: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/webhook_handler.py:113: in handle_event
    result = await self._handle_pull_request(payload)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/webhook_handler.py:167: in _handle_pull_request
    repo_config = await self._get_repo_config(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7f0f075f6030>
installation_id = 12345, repo_full_name = 'octocat/hello-world'
default_branch = 'main', owner_login = 'octocat', owner_type = 'User'

    async def _get_repo_config(
        self,
        installation_id: int,
        repo_full_name: str,
        default_branch: str,
        owner_login: str | None,
        owner_type: str | None,
    ) -> RepoConfig:
        """Get repository configuration from stampbot.toml.
    
        Reads from the repo's default branch first, then falls back to the
        organization-wide .github repo if available.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name
            default_branch: Repository default branch
            owner_login: Repository owner login
            owner_type: Repository owner type (Organization/User)
    
        Returns:
            RepoConfig instance (default if file not found)
        """
        with create_span(
            "webhook.get_repo_config",
            {"github.repo": repo_full_name, "github.ref": default_branch or "default"},
        ) as span:
            try:
                content = await run_in_threadpool(
                    github_client.get_repo_file,
                    installation_id,
                    repo_full_name,
                    "stampbot.toml",
                    default_branch,
                )
    
                if content:
                    return self._parse_repo_config(span, content, source_repo=repo_full_name)
    
                org_repo_full_name = None
                if (
                    owner_type == "Organization"
                    and owner_login
                    and repo_full_name != f"{owner_login}/.github"
                ):
                    org_repo_full_name = f"{owner_login}/.github"
                    org_content = await run_in_threadpool(
                        github_client.get_repo_file,
                        installation_id,
                        org_repo_full_name,
                        "stampbot.toml",
                        None,
                    )
                    if org_content:
                        return self._parse_repo_config(
                            span, org_content, source_repo=org_repo_full_name
                        )
    
                repo_config_loads_total.labels(status="default").inc()
                logger.info(
                    "No stampbot.toml found in %s%s, using defaults",
                    repo_full_name,
                    f" or {org_repo_full_name}" if org_repo_full_name else "",
                    extra={"repo": repo_full_name, "org_repo": org_repo_full_name},
                )
                add_span_attributes(span, {"config.result": "default"})
                set_span_ok(span)
                return RepoConfig.default()
    
>           except CosmicRayTestingException as e:
                   ^^^^^^^^^^^^^^^^^^^^^^^^^
E           NameError: name 'CosmicRayTestingException' is not defined

stampbot/webhook_handler.py:603: NameError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:50:46.472440Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:50:46.472440Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_get_repo_config_exception - NameError: name 'CosmicRayTestingException' is not defined
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 203 passed, 3 deselected in 1.81s
operator: core/ExceptionReplacer, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -630,7 +630,7 @@
         """
         try:
             repo_config = RepoConfig.from_toml(toml_content)
-        except ValueError as e:
+        except CosmicRayTestingException as e:
             repo_config_loads_total.labels(status="error").inc()
             add_span_attributes(
                 span,
........................................................................ [ 34%]
........................................................................ [ 68%]
................................................F
=================================== FAILURES ===================================
____________________ test_invalid_repo_config_posts_review _____________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f13e505f5f0>
mock_github_client = <MagicMock name='github_client' id='139723425049168'>

    @pytest.mark.asyncio
    async def test_invalid_repo_config_posts_review(webhook_handler, mock_github_client):
        """Test invalid repo config logs and posts a review comment."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
        mock_github_client.get_repo_file.return_value = 'chatops_required_permission = "invalid"'
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "error"
E       AssertionError: assert 'success' == 'error'
E         
E         - error
E         + success

tests/test_webhook_handler.py:516: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:38:12.978100Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T13:38:12.979126Z [warning  ] Error loading config from octocat/hello-world: name 'CosmicRayTestingException' is not defined, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=warning extra={'repo': 'octocat/hello-world', 'error': "name 'CosmicRayTestingException' is not defined"}
2026-03-27T13:38:12.979957Z [info     ] PR #42 has approval label: autoapprove _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:38:12.978100Z'}
WARNING  stampbot.webhook_handler:webhook_handler.py:605 {'extra': {'repo': 'octocat/hello-world', 'error': "name 'CosmicRayTestingException' is not defined"}, 'event': "Error loading config from octocat/hello-world: name 'CosmicRayTestingException' is not defined, using defaults", 'level': 'warning', 'timestamp': '2026-03-27T13:38:12.979126Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:278 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'PR #42 has approval label: autoapprove', 'level': 'info', 'timestamp': '2026-03-27T13:38:12.979957Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_invalid_repo_config_posts_review - AssertionError: assert 'success' == 'error'
  
  - error
  + success
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 192 passed, 3 deselected in 1.57s
operator: core/ExceptionReplacer, occurrence: 2
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -815,7 +815,7 @@
 
                 return success
 
-            except Exception as e:
+            except CosmicRayTestingException as e:
                 duration = time.time() - start_time
                 pr_dismissal_duration_seconds.observe(duration)
                 pr_dismissals_total.labels(trigger_type=trigger_type, status="failure").inc()
........................................................................ [ 34%]
........................................................................ [ 68%]
............................................................F
=================================== FAILURES ===================================
_______________________ test_dismiss_approvals_exception _______________________

self = <stampbot.webhook_handler.WebhookHandler object at 0x7fb7ad673ef0>
installation_id = 12345, repo_full_name = 'octocat/hello-world', pr_number = 42
message = 'Label autoapprove removed', trigger_type = 'label_removed'

    async def _dismiss_approvals(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        message: str,
        trigger_type: str,
    ) -> bool:
        """Dismiss bot approvals on a PR.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name
            pr_number: PR number
            message: Dismissal message
            trigger_type: What triggered the dismissal (label_removed, chatops)
    
        Returns:
            True if successful
        """
        with create_span(
            "webhook.dismiss_approvals",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "dismissal.trigger_type": trigger_type,
            },
        ) as span:
            start_time = time.time()
    
            try:
                # Find all bot reviews
>               review_ids = await run_in_threadpool(
                    github_client.find_bot_reviews,
                    installation_id,
                    repo_full_name,
                    pr_number,
                )

stampbot/webhook_handler.py:772: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/concurrency.py:32: in run_in_threadpool
    return await anyio.to_thread.run_sync(func)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/to_thread.py:63: in run_sync
    return await get_async_backend().run_sync_in_worker_thread(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/_backends/_asyncio.py:2502: in run_sync_in_worker_thread
    return await future
           ^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/_backends/_asyncio.py:986: in run
    result = context.run(func, *args)
             ^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1175: in __call__
    return self._mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1179: in _mock_call
    return self._execute_mock_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <MagicMock name='github_client.find_bot_reviews' id='140426859435680'>
args = (12345, 'octocat/hello-world', 42), kwargs = {}
effect = Exception('API error')

    def _execute_mock_call(self, /, *args, **kwargs):
        # separate from _increment_mock_call so that awaited functions are
        # executed separately from their call, also AsyncMock overrides this method
    
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
>               raise effect
E               Exception: API error

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/unittest/mock.py:1234: Exception

During handling of the above exception, another exception occurred:

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fb7ad673ef0>
mock_github_client = <MagicMock name='github_client' id='140426859434000'>

    @pytest.mark.asyncio
    async def test_dismiss_approvals_exception(webhook_handler, mock_github_client):
        """Test _dismiss_approvals handles exceptions gracefully."""
        payload = load_fixture("pr_unlabeled_autoapprove")
    
        # Make find_bot_reviews raise an exception
        mock_github_client.find_bot_reviews.side_effect = Exception("API error")
    
>       result = await webhook_handler.handle_event("pull_request", payload)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_webhook_handler.py:727: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/webhook_handler.py:113: in handle_event
    result = await self._handle_pull_request(payload)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/webhook_handler.py:332: in _handle_pull_request
    success = await self._dismiss_approvals(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7fb7ad673ef0>
installation_id = 12345, repo_full_name = 'octocat/hello-world', pr_number = 42
message = 'Label autoapprove removed', trigger_type = 'label_removed'

    async def _dismiss_approvals(
        self,
        installation_id: int,
        repo_full_name: str,
        pr_number: int,
        message: str,
        trigger_type: str,
    ) -> bool:
        """Dismiss bot approvals on a PR.
    
        Args:
            installation_id: GitHub App installation ID
            repo_full_name: Repository full name
            pr_number: PR number
            message: Dismissal message
            trigger_type: What triggered the dismissal (label_removed, chatops)
    
        Returns:
            True if successful
        """
        with create_span(
            "webhook.dismiss_approvals",
            {
                "github.repo": repo_full_name,
                "github.pr_number": pr_number,
                "dismissal.trigger_type": trigger_type,
            },
        ) as span:
            start_time = time.time()
    
            try:
                # Find all bot reviews
                review_ids = await run_in_threadpool(
                    github_client.find_bot_reviews,
                    installation_id,
                    repo_full_name,
                    pr_number,
                )
    
                add_span_attributes(span, {"dismissal.reviews_found": len(review_ids)})
    
                if not review_ids:
                    logger.info(
                        "No bot approvals found on PR #%d",
                        pr_number,
                        extra={"repo": repo_full_name, "pr_number": pr_number},
                    )
                    duration = time.time() - start_time
                    pr_dismissal_duration_seconds.observe(duration)
                    pr_dismissals_total.labels(trigger_type=trigger_type, status="success").inc()
                    add_span_attributes(span, {"dismissal.result": "no_reviews"})
                    set_span_ok(span)
                    return True
    
                # Dismiss each review
                success = True
                for review_id in review_ids:
                    result = await run_in_threadpool(
                        github_client.dismiss_approval,
                        installation_id,
                        repo_full_name,
                        pr_number,
                        review_id,
                        message,
                    )
                    success = success and result
    
                duration = time.time() - start_time
                pr_dismissal_duration_seconds.observe(duration)
    
                status = "success" if success else "failure"
                pr_dismissals_total.labels(trigger_type=trigger_type, status=status).inc()
    
                add_span_attributes(span, {"dismissal.result": status})
                set_span_ok(span)
    
                return success
    
>           except CosmicRayTestingException as e:
                   ^^^^^^^^^^^^^^^^^^^^^^^^^
E           NameError: name 'CosmicRayTestingException' is not defined

stampbot/webhook_handler.py:818: NameError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:26:17.269905Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-03-27T13:26:17.270459Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T13:26:17.271020Z [info     ] Approval label autoapprove removed from PR #42 _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'unlabeled'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:26:17.269905Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:26:17.270459Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:320 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'Approval label autoapprove removed from PR #42', 'level': 'info', 'timestamp': '2026-03-27T13:26:17.271020Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_dismiss_approvals_exception - NameError: name 'CosmicRayTestingException' is not defined
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 204 passed, 3 deselected in 1.81s
operator: core/NumberReplacer, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -29,7 +29,7 @@
 logger = get_logger(__name__)
 
 # Security: Maximum length for user-controlled strings to prevent DoS
-MAX_COMMENT_LENGTH = 65536  # 64KB - generous but prevents abuse
+MAX_COMMENT_LENGTH = 65537  # 64KB - generous but prevents abuse
 
 
 class WebhookHandler:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/NumberReplacer, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -29,7 +29,7 @@
 logger = get_logger(__name__)
 
 # Security: Maximum length for user-controlled strings to prevent DoS
-MAX_COMMENT_LENGTH = 65536  # 64KB - generous but prevents abuse
+MAX_COMMENT_LENGTH = 65535  # 64KB - generous but prevents abuse
 
 
 class WebhookHandler:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.63s
operator: core/NumberReplacer, occurrence: 2
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -153,7 +153,7 @@
             "webhook.handle_pull_request",
             {
                 "github.repo": repo_full_name or "unknown",
-                "github.pr_number": pr_number or 0,
+                "github.pr_number": pr_number or 1,
                 "github.action": action or "unknown",
             },
         ) as span:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.61s
operator: core/NumberReplacer, occurrence: 3
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -153,7 +153,7 @@
             "webhook.handle_pull_request",
             {
                 "github.repo": repo_full_name or "unknown",
-                "github.pr_number": pr_number or 0,
+                "github.pr_number": pr_number or -1,
                 "github.action": action or "unknown",
             },
         ) as span:
........................................................................ [ 34%]
........................................................................ [ 68%]
...................................................................      [100%]
211 passed, 3 deselected in 1.62s
operator: core/NumberReplacer, occurrence: 4
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -383,7 +383,7 @@
         if "pull_request" in payload.get("issue", {}):
             pr_url = payload["issue"]["pull_request"]["url"]
             # Extract PR number from URL
-            pr_number = int(pr_url.split("/")[-1])
+            pr_number = int(pr_url.split("/")[- 2])
         elif "pull_request" in payload:
             pr_number = payload["pull_request"]["number"]
         else:
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fb7e23ef800>
mock_github_client = <MagicMock name='github_client' id='140427738705472'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
>       result = await webhook_handler.handle_event("issue_comment", payload)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_webhook_handler.py:257: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/webhook_handler.py:119: in handle_event
    result = await self._handle_pr_comment(payload)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7fb7e23ef800>
payload = {'action': 'created', 'comment': {'body': '@stampbot approve\n\nLGTM, approving this PR.', 'created_at': '2025-01-10T1...U='}, 'issue': {'assignee': None, 'assignees': [], 'body': 'This PR adds a new feature.', 'closed_at': None, ...}, ...}

    async def _handle_pr_comment(self, payload: dict[str, Any]) -> dict[str, Any]:
        """Handle PR comment events for chatops.
    
        Args:
            payload: Event payload
    
        Returns:
            Response dictionary
        """
        comment = payload.get("comment", {})
        comment_body = comment.get("body", "")
    
        # Security: limit comment length to prevent DoS
        if len(comment_body) > MAX_COMMENT_LENGTH:
            return {"status": "ignored", "message": "Comment too long"}
    
        comment_body = comment_body.lower().strip()
    
        # Check if bot is mentioned
        if "@stampbot" not in comment_body:
            return {"status": "ignored", "message": "Bot not mentioned"}
    
        # Extract PR info
        if "pull_request" in payload.get("issue", {}):
            pr_url = payload["issue"]["pull_request"]["url"]
            # Extract PR number from URL
>           pr_number = int(pr_url.split("/")[- 2])
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^
E           ValueError: invalid literal for int() with base 10: 'pulls'

stampbot/webhook_handler.py:386: ValueError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:29:59.371507Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T13:29:59.371507Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - ValueError: invalid literal for int() with base 10: 'pulls'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.16s
operator: core/NumberReplacer, occurrence: 5
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -383,7 +383,7 @@
         if "pull_request" in payload.get("issue", {}):
             pr_url = payload["issue"]["pull_request"]["url"]
             # Extract PR number from URL
-            pr_number = int(pr_url.split("/")[-1])
+            pr_number = int(pr_url.split("/")[- 0])
         elif "pull_request" in payload:
             pr_number = payload["pull_request"]["number"]
         else:
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f921d356150>
mock_github_client = <MagicMock name='github_client' id='140265532534336'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
>       result = await webhook_handler.handle_event("issue_comment", payload)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_webhook_handler.py:257: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/webhook_handler.py:119: in handle_event
    result = await self._handle_pr_comment(payload)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7f921d356150>
payload = {'action': 'created', 'comment': {'body': '@stampbot approve\n\nLGTM, approving this PR.', 'created_at': '2025-01-10T1...U='}, 'issue': {'assignee': None, 'assignees': [], 'body': 'This PR adds a new feature.', 'closed_at': None, ...}, ...}

    async def _handle_pr_comment(self, payload: dict[str, Any]) -> dict[str, Any]:
        """Handle PR comment events for chatops.
    
        Args:
            payload: Event payload
    
        Returns:
            Response dictionary
        """
        comment = payload.get("comment", {})
        comment_body = comment.get("body", "")
    
        # Security: limit comment length to prevent DoS
        if len(comment_body) > MAX_COMMENT_LENGTH:
            return {"status": "ignored", "message": "Comment too long"}
    
        comment_body = comment_body.lower().strip()
    
        # Check if bot is mentioned
        if "@stampbot" not in comment_body:
            return {"status": "ignored", "message": "Bot not mentioned"}
    
        # Extract PR info
        if "pull_request" in payload.get("issue", {}):
            pr_url = payload["issue"]["pull_request"]["url"]
            # Extract PR number from URL
>           pr_number = int(pr_url.split("/")[- 0])
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^
E           ValueError: invalid literal for int() with base 10: 'https:'

stampbot/webhook_handler.py:386: ValueError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:57:11.175707Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T13:57:11.175707Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - ValueError: invalid literal for int() with base 10: 'https:'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.17s
operator: core/NumberReplacer, occurrence: 6
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -448,7 +448,7 @@
                 set_span_ok(span)
                 return {"status": "ignored", "message": "No command found"}
 
-            command = command_match.group(1).lower()
+            command = command_match.group( 2).lower()
             add_span_attributes(span, {"chatops.command": command})
 
             if command in repo_config.approve_commands + repo_config.unapprove_commands:
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f63e9a56150>
mock_github_client = <MagicMock name='github_client' id='140067098963520'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
>       result = await webhook_handler.handle_event("issue_comment", payload)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

tests/test_webhook_handler.py:257: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
stampbot/webhook_handler.py:119: in handle_event
    result = await self._handle_pr_comment(payload)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <stampbot.webhook_handler.WebhookHandler object at 0x7f63e9a56150>
payload = {'action': 'created', 'comment': {'body': '@stampbot approve\n\nLGTM, approving this PR.', 'created_at': '2025-01-10T1...U='}, 'issue': {'assignee': None, 'assignees': [], 'body': 'This PR adds a new feature.', 'closed_at': None, ...}, ...}

    async def _handle_pr_comment(self, payload: dict[str, Any]) -> dict[str, Any]:
        """Handle PR comment events for chatops.
    
        Args:
            payload: Event payload
    
        Returns:
            Response dictionary
        """
        comment = payload.get("comment", {})
        comment_body = comment.get("body", "")
    
        # Security: limit comment length to prevent DoS
        if len(comment_body) > MAX_COMMENT_LENGTH:
            return {"status": "ignored", "message": "Comment too long"}
    
        comment_body = comment_body.lower().strip()
    
        # Check if bot is mentioned
        if "@stampbot" not in comment_body:
            return {"status": "ignored", "message": "Bot not mentioned"}
    
        # Extract PR info
        if "pull_request" in payload.get("issue", {}):
            pr_url = payload["issue"]["pull_request"]["url"]
            # Extract PR number from URL
            pr_number = int(pr_url.split("/")[-1])
        elif "pull_request" in payload:
            pr_number = payload["pull_request"]["number"]
        else:
            return {"status": "error", "message": "Not a PR comment"}
    
        repo = payload.get("repository", {})
        repo_full_name = repo.get("full_name")
        repo_default_branch = repo.get("default_branch") or "main"
        repo_owner = repo.get("owner", {})
        owner_login = repo_owner.get("login")
        owner_type = repo_owner.get("type")
        installation_id = payload.get("installation", {}).get("id")
        commenter = comment.get("user", {}).get("login", "unknown")
    
        with create_span(
            "webhook.handle_chatops",
            {
                "github.repo": repo_full_name or "unknown",
                "github.pr_number": pr_number,
                "chatops.commenter": commenter,
            },
        ) as span:
            if not all([pr_number, repo_full_name, installation_id]):
                logger.warning("Missing required fields in comment event")
                add_span_attributes(span, {"chatops.result": "missing_fields"})
                set_span_ok(span)
                return {"status": "error", "message": "Missing required fields"}
    
            # Get repository configuration
            repo_config = await self._get_repo_config(
                installation_id,
                repo_full_name,
                repo_default_branch,
                owner_login,
                owner_type,
            )
    
            if repo_config.config_error:
                add_span_attributes(
                    span,
                    {
                        "chatops.result": "config_error",
                        "chatops.config_error": repo_config.config_error,
                    },
                )
                set_span_ok(span)
                return {
                    "status": "error",
                    "message": "Invalid repository configuration",
                }
    
            if not repo_config.chatops_enabled:
                add_span_attributes(span, {"chatops.result": "disabled"})
                set_span_ok(span)
                return {"status": "ignored", "message": "Chatops not enabled"}
    
            # Parse command
            command_match = re.search(r"@stampbot\s+(\w+)", comment_body)
            if not command_match:
                chatops_commands_total.labels(command="none", status="ignored").inc()
                add_span_attributes(span, {"chatops.result": "no_command"})
                set_span_ok(span)
                return {"status": "ignored", "message": "No command found"}
    
>           command = command_match.group( 2).lower()
                      ^^^^^^^^^^^^^^^^^^^^^^^
E           IndexError: no such group

stampbot/webhook_handler.py:451: IndexError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:52:37.255526Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-03-27T13:52:37.256086Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T13:52:37.255526Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:52:37.256086Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - IndexError: no such group
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.17s
operator: core/NumberReplacer, occurrence: 7
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -448,7 +448,7 @@
                 set_span_ok(span)
                 return {"status": "ignored", "message": "No command found"}
 
-            command = command_match.group(1).lower()
+            command = command_match.group( 0).lower()
             add_span_attributes(span, {"chatops.command": command})
 
             if command in repo_config.approve_commands + repo_config.unapprove_commands:
........................................................................ [ 34%]
........................................................................ [ 68%]
.................................F
=================================== FAILURES ===================================
__________________________ test_issue_comment_approve __________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f62c2b03800>
mock_github_client = <MagicMock name='github_client' id='140062139117120'>

    @pytest.mark.asyncio
    async def test_issue_comment_approve(webhook_handler, mock_github_client):
        """Test @stampbot approve command."""
        payload = load_fixture("issue_comment_approve")
    
        result = await webhook_handler.handle_event("issue_comment", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'ignored' == 'success'
E         
E         - success
E         + ignored

tests/test_webhook_handler.py:259: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:08:35.311297Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-03-27T14:08:35.311858Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'issue_comment', 'action': 'created'}, 'event': 'Received issue_comment event', 'level': 'info', 'timestamp': '2026-03-27T14:08:35.311297Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T14:08:35.311858Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_issue_comment_approve - AssertionError: assert 'ignored' == 'success'
  
  - success
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 177 passed, 3 deselected in 1.12s
operator: core/RemoveDecorator, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -38,8 +38,6 @@
     def __init__(self) -> None:
         """Initialize webhook handler (lazy initialization)."""
         self._webhook_secret: bytes | None = None
-
-    @property
     def webhook_secret(self) -> bytes:
         """Get webhook secret, initializing if needed.
 
........................................................................ [ 34%]
........................F
=================================== FAILURES ===================================
_______________________ test_request_with_content_length _______________________

test_client = <starlette.testclient.TestClient object at 0x7fc287ec3770>

    def test_request_with_content_length(test_client: TestClient):
        """Test that requests with Content-Length header are tracked."""
        # POST with a body will have Content-Length set automatically
>       response = test_client.post(
            "/webhook",
            content=b'{"test": "data"}',
            headers={
                "Content-Type": "application/json",
                "X-GitHub-Event": "ping",
                "X-Hub-Signature-256": "sha256=invalid",
            },
        )

tests/test_main.py:69: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:546: in post
    return super().post(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1144: in post
    return self.request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:445: in request
    return super().request(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:825: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:914: in send
    response = self._send_handling_auth(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:942: in _send_handling_auth
    response = self._send_handling_redirects(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:979: in _send_handling_redirects
    response = self._send_single_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/httpx/_client.py:1014: in _send_single_request
    response = transport.handle_request(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:348: in handle_request
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/testclient.py:345: in handle_request
    portal.call(self.app, scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:334: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:443: in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/concurrent/futures/_base.py:395: in __get_result
    raise self._exception
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/anyio/from_thread.py:259: in _call_func
    retval = await retval_or_awaitable
             ^^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/applications.py:1135: in __call__
    await super().__call__(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/applications.py:107: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:186: in __call__
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/errors.py:164: in __call__
    await self.app(scope, receive, _send)
stampbot/main.py:205: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:191: in __call__
    with recv_stream, send_stream, collapse_excgroups():
                                   ^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/contextlib.py:162: in __exit__
    self.gen.throw(value)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_utils.py:85: in collapse_excgroups
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:193: in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:120: in metrics_middleware
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:168: in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/base.py:144: in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/middleware/exceptions.py:63: in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/middleware/asyncexitstack.py:18: in __call__
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:716: in __call__
    await self.middleware_stack(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:736: in app
    await route.handle(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/routing.py:290: in handle
    await self.app(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:115: in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:53: in wrapped_app
    raise exc
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/starlette/_exception_handler.py:42: in wrapped_app
    await app(scope, receive, sender)
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:101: in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:355: in app
    raw_response = await run_endpoint_function(
../../../.cache/pypoetry/virtualenvs/stampbot-q2WA0ezo-py3.14/lib/python3.14/site-packages/fastapi/routing.py:243: in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/main.py:296: in webhook
    if not webhook_handler.verify_signature(body, x_hub_signature_256):
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
stampbot/webhook_handler.py:72: in verify_signature
    "sha256=" + hmac.new(self.webhook_secret, payload, hashlib.sha256).hexdigest()
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/hmac.py:218: in new
    return HMAC(key, msg, digestmod)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <hmac.HMAC object at 0x7fc287cc9a30>
key = <bound method WebhookHandler.webhook_secret of <stampbot.webhook_handler.WebhookHandler object at 0x7fc287ecacf0>>
msg = b'{"test": "data"}', digestmod = <built-in function openssl_sha256>

    def __init__(self, key, msg=None, digestmod=''):
        """Create a new HMAC object.
    
        key: bytes or buffer, key for the keyed hash object.
        msg: bytes or buffer, Initial input for the hash or None.
        digestmod: A hash name suitable for hashlib.new(). *OR*
                   A hashlib constructor returning a new hash object. *OR*
                   A module supporting PEP 247.
    
                   Required as of 3.8, despite its position after the optional
                   msg argument.  Passing it as a keyword argument is
                   recommended, though not required for legacy API reasons.
        """
    
        if not isinstance(key, (bytes, bytearray)):
>           raise TypeError(f"key: expected bytes or bytearray, "
                            f"but got {type(key).__name__!r}")
E           TypeError: key: expected bytes or bytearray, but got 'method'

/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/hmac.py:71: TypeError
=========================== short test summary info ============================
FAILED tests/test_main.py::test_request_with_content_length - TypeError: key: expected bytes or bytearray, but got 'method'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 96 passed, 3 deselected in 1.17s
operator: core/ZeroIterationForLoop, occurrence: 0
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -207,7 +207,7 @@
                 }
 
             if action == "opened":
-                for label in repo_config.approval_labels:
+                for label in []:
                     label_exists = await run_in_threadpool(
                         github_client.repo_has_label,
                         installation_id,
........................................................................ [ 34%]
........................................................................ [ 68%]
.....................F
=================================== FAILURES ===================================
__________________ test_pr_opened_missing_label_logs_warning ___________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7f45718b0830>
mock_github_client = <MagicMock name='github_client' id='139936174419184'>

    @pytest.mark.asyncio
    async def test_pr_opened_missing_label_logs_warning(webhook_handler, mock_github_client):
        """Test missing approval label triggers label check."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
        mock_github_client.repo_has_label.return_value = False
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
        assert result["status"] == "success"
>       assert mock_github_client.repo_has_label.call_count == 2
E       AssertionError: assert 0 == 2
E        +  where 0 = <MagicMock name='github_client.repo_has_label' id='139936174416496'>.call_count
E        +    where <MagicMock name='github_client.repo_has_label' id='139936174416496'> = <MagicMock name='github_client' id='139936174419184'>.repo_has_label

tests/test_webhook_handler.py:101: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T14:01:12.718461Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T14:01:12.719020Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T14:01:12.719535Z [info     ] PR #42 has approval label: autoapprove _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T14:01:12.718461Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T14:01:12.719020Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:278 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'PR #42 has approval label: autoapprove', 'level': 'info', 'timestamp': '2026-03-27T14:01:12.719535Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_opened_missing_label_logs_warning - AssertionError: assert 0 == 2
 +  where 0 = <MagicMock name='github_client.repo_has_label' id='139936174416496'>.call_count
 +    where <MagicMock name='github_client.repo_has_label' id='139936174416496'> = <MagicMock name='github_client' id='139936174419184'>.repo_has_label
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 165 passed, 3 deselected in 1.09s
operator: core/ZeroIterationForLoop, occurrence: 1
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -233,7 +233,7 @@
                 pr_title = pr.get("title", "")
                 pr_author = pr.get("user", {}).get("login", "")
 
-                for label in labels:
+                for label in []:
                     if label in repo_config.approval_labels:
                         # Check if team membership verification is needed
                         author_team_slugs: list[str] | None = None
........................................................................ [ 34%]
........................................................................ [ 68%]
...................F
=================================== FAILURES ===================================
____________________ test_pr_opened_with_autoapprove_label _____________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7ff67ee00050>
mock_github_client = <MagicMock name='github_client' id='140696667866176'>

    @pytest.mark.asyncio
    async def test_pr_opened_with_autoapprove_label(webhook_handler, mock_github_client):
        """Test PR opened with autoapprove label triggers approval."""
        payload = load_fixture("pr_opened_with_autoapprove_label")
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
>       assert result["status"] == "success"
E       AssertionError: assert 'ignored' == 'success'
E         
E         - success
E         + ignored

tests/test_webhook_handler.py:70: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:59:26.234081Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-03-27T13:59:26.234756Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'opened'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:59:26.234081Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:59:26.234756Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_opened_with_autoapprove_label - AssertionError: assert 'ignored' == 'success'
  
  - success
  + ignored
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 163 passed, 3 deselected in 1.06s
operator: core/ZeroIterationForLoop, occurrence: 2
--- mutation diff ---
--- astampbot/webhook_handler.py
+++ bstampbot/webhook_handler.py
@@ -793,7 +793,7 @@
 
                 # Dismiss each review
                 success = True
-                for review_id in review_ids:
+                for review_id in []:
                     result = await run_in_threadpool(
                         github_client.dismiss_approval,
                         installation_id,
........................................................................ [ 34%]
........................................................................ [ 68%]
.........................F
=================================== FAILURES ===================================
________________________ test_pr_unlabeled_autoapprove _________________________

webhook_handler = <stampbot.webhook_handler.WebhookHandler object at 0x7fa7cc8f5350>
mock_github_client = <MagicMock name='github_client' id='140358733143344'>

    @pytest.mark.asyncio
    async def test_pr_unlabeled_autoapprove(webhook_handler, mock_github_client):
        """Test removing autoapprove label dismisses approvals."""
        payload = load_fixture("pr_unlabeled_autoapprove")
        mock_github_client.find_bot_reviews.return_value = [111, 222]  # Mock review IDs
    
        result = await webhook_handler.handle_event("pull_request", payload)
    
        assert result["status"] == "success"
        assert "dismissed" in result["message"].lower()
        mock_github_client.find_bot_reviews.assert_called_once()
>       assert mock_github_client.dismiss_approval.call_count == 2
E       AssertionError: assert 0 == 2
E        +  where 0 = <MagicMock name='github_client.dismiss_approval' id='140358733144688'>.call_count
E        +    where <MagicMock name='github_client.dismiss_approval' id='140358733144688'> = <MagicMock name='github_client' id='140358733143344'>.dismiss_approval

tests/test_webhook_handler.py:155: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-03-27T13:40:24.470997Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-03-27T13:40:24.471530Z [info     ] No stampbot.toml found in octocat/hello-world, using defaults _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'org_repo': None}
2026-03-27T13:40:24.472091Z [info     ] Approval label autoapprove removed from PR #42 _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}
------------------------------ Captured log call -------------------------------
INFO     stampbot.webhook_handler:webhook_handler.py:105 {'extra': {'event_type': 'pull_request', 'action': 'unlabeled'}, 'event': 'Received pull_request event', 'level': 'info', 'timestamp': '2026-03-27T13:40:24.470997Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:593 {'extra': {'repo': 'octocat/hello-world', 'org_repo': None}, 'event': 'No stampbot.toml found in octocat/hello-world, using defaults', 'level': 'info', 'timestamp': '2026-03-27T13:40:24.471530Z'}
INFO     stampbot.webhook_handler:webhook_handler.py:320 {'extra': {'repo': 'octocat/hello-world', 'pr_number': 42, 'label': 'autoapprove'}, 'event': 'Approval label autoapprove removed from PR #42', 'level': 'info', 'timestamp': '2026-03-27T13:40:24.472091Z'}
=========================== short test summary info ============================
FAILED tests/test_webhook_handler.py::test_pr_unlabeled_autoapprove - AssertionError: assert 0 == 2
 +  where 0 = <MagicMock name='github_client.dismiss_approval' id='140358733144688'>.call_count
 +    where <MagicMock name='github_client.dismiss_approval' id='140358733144688'> = <MagicMock name='github_client' id='140358733143344'>.dismiss_approval
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 169 passed, 3 deselected in 1.09s