Cosmic Ray Report

Date time: 16/05/2026 20:10:19

Total jobs: 1222

Complete: 1222 (100.00%)

Surviving mutants: 556 (45.50%)

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 0x7fa14c8a4190>

    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.13s
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 0x7f328579c190>

    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_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 0x7f065459c190>

    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: 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.20s
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 0x7f0e59eac190>

    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: 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 0x7f2042194190>

    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_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 0x7f49b889c190>

    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.17s
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 0x7f5218694190>

    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 0x7ff73d89c190>

    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.19s
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 0x7fa3fb798190>

    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.18s
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 0x7f2e1da8c190>

    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.16s
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 0x7fdc8baa4190>

    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_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 0x7f3190d9c190>

    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.15s
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 0x7f50ae994190>

    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 0x7f7a7fc98190>

    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.21s
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 0x7f06f679c190>

    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_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 0x7fc8bb198190>

    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_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 0x7f5192074190>

    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_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 0x7f3b51984190>

    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.19s
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 0x7fef26ea4190>

    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_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 0x7f960649c190>

    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: 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 0x7f0e5b774190>

    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_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 0x7f6f9d49c190>

    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_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.60s
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.61s
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.62s
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.62s
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.62s
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.62s
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.60s
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.67s
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 0x7fbbe8d830e0>
call_next = <function BaseHTTPMiddleware.__call__.<locals>.call_next at 0x7fbbe8d86820>

    @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-05-16T20:05:03.298968Z [info     ] OpenTelemetry disabled         _logger=<_FixedFindCallerLogger stampbot.telemetry (INFO)> _name=info
2026-05-16T20:05:03.319457Z [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-05-16T20:05:03.319785Z [info     ] GitHub App credentials configured successfully _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=info
2026-05-16T20:05:03.322081Z [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.10s
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 0x7fd34b5be9c0>

    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-05-16T19:46:48.859490Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-05-16T19:46:48.859710Z [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-05-16T19:46:48.860502Z [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-05-16T19:46:48.859490Z'}
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-05-16T19:46:48.859710Z'}
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 0x7f022ac7f0e0>
call_next = <function BaseHTTPMiddleware.__call__.<locals>.call_next at 0x7f022ac82820>

    @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-05-16T19:31:41.616338Z [info     ] OpenTelemetry disabled         _logger=<_FixedFindCallerLogger stampbot.telemetry (INFO)> _name=info
2026-05-16T19:31:41.635051Z [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-05-16T19:31:41.635366Z [info     ] GitHub App credentials configured successfully _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=info
2026-05-16T19:31:41.637794Z [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_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 0x7f474e0aa9c0>

    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-05-16T19:25:04.081728Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-05-16T19:25:04.081925Z [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-05-16T19:25:04.082705Z [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-05-16T19:25:04.081728Z'}
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-05-16T19:25:04.081925Z'}
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 0x7f1f1a57b0e0>
call_next = <function BaseHTTPMiddleware.__call__.<locals>.call_next at 0x7f1f1a57e6c0>

    @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-05-16T19:31:01.090374Z [info     ] OpenTelemetry disabled         _logger=<_FixedFindCallerLogger stampbot.telemetry (INFO)> _name=info
2026-05-16T19:31:01.109042Z [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-05-16T19:31:01.109379Z [info     ] GitHub App credentials configured successfully _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=info
2026-05-16T19:31:01.111475Z [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_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 0x7f8366fba9c0>

    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-05-16T19:37:43.572104Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-05-16T19:37:43.572359Z [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-05-16T19:37:43.573120Z [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-05-16T19:37:43.572104Z'}
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-05-16T19:37:43.572359Z'}
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 0x7f690ee9b0e0>
call_next = <function BaseHTTPMiddleware.__call__.<locals>.call_next at 0x7f690ee9e820>

    @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-05-16T19:39:04.156707Z [info     ] OpenTelemetry disabled         _logger=<_FixedFindCallerLogger stampbot.telemetry (INFO)> _name=info
2026-05-16T19:39:04.175536Z [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-05-16T19:39:04.175833Z [info     ] GitHub App credentials configured successfully _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=info
2026-05-16T19:39:04.178016Z [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_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 0x7fcb932b29c0>

    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-05-16T19:32:23.606210Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-05-16T19:32:23.606417Z [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-05-16T19:32:23.607175Z [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-05-16T19:32:23.606210Z'}
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-05-16T19:32:23.606417Z'}
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_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 0x7fb887b670e0>
call_next = <function BaseHTTPMiddleware.__call__.<locals>.call_next at 0x7fb887b6a820>

    @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-05-16T19:42:22.183082Z [info     ] OpenTelemetry disabled         _logger=<_FixedFindCallerLogger stampbot.telemetry (INFO)> _name=info
2026-05-16T19:42:22.202798Z [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-05-16T19:42:22.203161Z [info     ] GitHub App credentials configured successfully _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=info
2026-05-16T19:42:22.205440Z [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 0x7f552acc29c0>

    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-05-16T19:41:32.534096Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-05-16T19:41:32.534338Z [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-05-16T19:41:32.535112Z [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-05-16T19:41:32.534096Z'}
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-05-16T19:41:32.534338Z'}
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.87s
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 0x7f1152b8b0e0>
call_next = <function BaseHTTPMiddleware.__call__.<locals>.call_next at 0x7f1152b8e820>

    @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-05-16T20:06:16.338423Z [info     ] OpenTelemetry disabled         _logger=<_FixedFindCallerLogger stampbot.telemetry (INFO)> _name=info
2026-05-16T20:06:16.357653Z [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-05-16T20:06:16.357956Z [info     ] GitHub App credentials configured successfully _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=info
2026-05-16T20:06:16.360150Z [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_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 0x7f487d8b29c0>

    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-05-16T19:49:01.674276Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-05-16T19:49:01.674488Z [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-05-16T19:49:01.675300Z [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-05-16T19:49:01.674276Z'}
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-05-16T19:49:01.674488Z'}
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.60s
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 0x7f23219af770>

    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-05-16T19:51:05.840813Z [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_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 0x7fcd531af770>

    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-05-16T19:37:27.304396Z [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_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 0x7f98c14a3770>

    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-05-16T19:37:41.664500Z [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_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 0x7f8f566b7770>

    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-05-16T20:00:59.270681Z [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_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 0x7f2db71b7020>

    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-05-16T20:10:06.501808Z [warning  ] Invalid webhook signature      _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=warning client_ip=testclient
2026-05-16T20:10:06.502615Z [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-05-16T20:10:06.501808Z'}
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.89s
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 0x7f437419f770>

    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-05-16T19:53:09.968057Z [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/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 0x7f19ecbc3020>

    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-05-16T19:52:13.268524Z [warning  ] Invalid webhook signature      _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=warning client_ip=testclient
2026-05-16T19:52:13.269334Z [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-05-16T19:52:13.268524Z'}
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_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.61s
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 0x7ffb234a7770>

    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-05-16T20:09:45.177801Z [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.88s
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: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 0x7f2ad4166900>
scope = {'app': <fastapi.applications.FastAPI object at 0x7f2ad43970e0>, '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-05-16T19:28:24.668944Z [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.95s
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 0x7f75de913780>('client_ip')
E            +    where <built-in method get of dict object at 0x7f75de913780> = {}.get

tests/test_main.py:296: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-05-16T19:39:36.573772Z [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 0x7f75de913780>('client_ip')
 +    where <built-in method get of dict object at 0x7f75de913780> = {}.get
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 107 passed, 3 deselected in 0.86s
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.62s
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: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 0x7fadf3b72900>
scope = {'app': <fastapi.applications.FastAPI object at 0x7fadf3da30e0>, '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-05-16T19:51:58.150502Z [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.95s
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 0x7f069096a900>
scope = {'app': <fastapi.applications.FastAPI object at 0x7f0690b9b0e0>, '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-05-16T19:53:02.020161Z [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.98s
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.64s
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 0x7fb671c8aa50>
scope = {'app': <fastapi.applications.FastAPI object at 0x7fb671ebf230>, '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-05-16T19:53:22.134593Z [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 0x7f7be7c87020>

    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-05-16T20:09:04.280737Z [warning  ] Invalid webhook signature      _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=warning client_ip=testclient
2026-05-16T20:09:04.281544Z [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-05-16T20:09:04.280737Z'}
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.91s
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 0x7f9382fb78a0>

    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-05-16T19:54:19.970225Z [warning  ] Invalid webhook signature      _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=warning client_ip=testclient
2026-05-16T19:54:19.971700Z [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-05-16T19:54:19.970225Z'}
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.89s
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 0x7f4e908b7770>

    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-05-16T20:05:18.836585Z [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_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 0x7f3d70d97770>

    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-05-16T19:27:44.779742Z [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.82s
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 0x7f76738a7770>

    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-05-16T19:23:07.935030Z [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 0x7f4665aa3770>

    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-05-16T19:34:56.107122Z [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.82s
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 0x7fa7259af770>

    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-05-16T19:28:39.847705Z [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_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 0x7ff54f3b3770>

    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-05-16T19:52:52.279762Z [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_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.61s
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.61s
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 0x7fb3223bb020>

    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-05-16T19:31:39.780475Z [warning  ] Invalid webhook signature      _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=warning client_ip=testclient
2026-05-16T19:31:39.781283Z [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-05-16T19:31:39.780475Z'}
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/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 0x7f40e12c38a0>

    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-05-16T19:44:12.675455Z [warning  ] Invalid webhook signature      _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=warning client_ip=testclient
2026-05-16T19:44:12.676542Z [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-05-16T19:44:12.675455Z'}
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_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 0x7fdc87dab770>

    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-05-16T19:43:26.570015Z [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_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 0x7f1145db3770>

    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-05-16T19:38:47.669552Z [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/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.61s
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-05-16T19:31:03.250397Z [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 0x7f97eacb4190>

    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 0x7f97f2362f90>
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-05-16T19:52:45.274373Z [info     ] HTTP Request: GET http://testserver/ "HTTP/1.1 307 Temporary Redirect"
2026-05-16T19:52:45.276600Z [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.87s
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 0x7f3e403a3770>

    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-05-16T19:40:00.042618Z [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 0x7fe839183770>

    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-05-16T19:34:27.027542Z [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.83s
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 0x7f96b4b93770>

    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-05-16T19:31:12.656595Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-05-16T19:31:12.657562Z [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-05-16T19:31:12.656595Z'}
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/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 0x7f7ecbe5fb10>
test_client = <starlette.testclient.TestClient object at 0x7f7ec6c16be0>

    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-05-16T19:39:55.326944Z [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 0x7f74d45f7ed0>
test_client = <starlette.testclient.TestClient object at 0x7f74cbe17bd0>

    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-05-16T19:45:53.548678Z [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.64s
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 0x7f7edae9f0e0>
call_next = <function BaseHTTPMiddleware.__call__.<locals>.call_next at 0x7f7edaea2820>

    @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-05-16T19:41:41.822516Z [info     ] OpenTelemetry disabled         _logger=<_FixedFindCallerLogger stampbot.telemetry (INFO)> _name=info
2026-05-16T19:41:41.841816Z [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-05-16T19:41:41.842128Z [info     ] GitHub App credentials configured successfully _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=info
2026-05-16T19:41:41.844456Z [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.05s
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 0x7ff1d4e16cf0>
call_next = <function BaseHTTPMiddleware.__call__.<locals>.call_next at 0x7ff1d4e052d0>

    @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.09s
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 0x7f5c68582900>
scope = {'app': <fastapi.applications.FastAPI object at 0x7f5c687b30e0>, '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-05-16T19:45:11.876903Z [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-05-16T19:54:40.048836Z [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.89s
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 0x7fedd587a660>
scope = {'app': <fastapi.applications.FastAPI object at 0x7fedd5aab0e0>, '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 0x7fedd587ddd0>
send = <function ServerErrorMiddleware.__call__.<locals>._send at 0x7fedd587e6c0>

    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-05-16T19:30:37.772816Z [info     ] OpenTelemetry disabled         _logger=<_FixedFindCallerLogger stampbot.telemetry (INFO)> _name=info
2026-05-16T19:30:37.792278Z [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-05-16T19:30:37.792586Z [info     ] GitHub App credentials configured successfully _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=info
2026-05-16T19:30:37.793919Z [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.01s
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-05-16T19:39:50.372463Z [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.86s
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 0x7fb29b21e700>('client_ip')
E            +    where <built-in method get of dict object at 0x7fb29b21e700> = {}.get

tests/test_main.py:296: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-05-16T19:53:49.331774Z [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 0x7fb29b21e700>('client_ip')
 +    where <built-in method get of dict object at 0x7fb29b21e700> = {}.get
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 107 passed, 3 deselected in 0.89s
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 0x7f6851e8c190>

    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 0x7f6859562f90>
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-05-16T19:59:32.438891Z [info     ] HTTP Request: GET http://testserver/ "HTTP/1.1 307 Temporary Redirect"
2026-05-16T19:59:32.441179Z [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 0x7f430c9a7770>

    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-05-16T19:29:33.948904Z [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 0x7fe8eeb83770>

    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-05-16T19:50:04.325858Z [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 0x7f114b3ab020>

    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-05-16T19:41:14.569885Z [warning  ] Invalid webhook signature      _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=warning client_ip=testclient
2026-05-16T19:41:14.570743Z [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-05-16T19:41:14.569885Z'}
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.91s
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 0x7f0fb3f87770>

    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-05-16T19:53:42.503264Z [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/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 0x7f2d726ab770>

    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-05-16T19:29:40.311091Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-05-16T19:29:40.312042Z [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-05-16T19:29:40.311091Z'}
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.82s
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 0x7fcef21c3b10>
test_client = <starlette.testclient.TestClient object at 0x7fceecf12be0>

    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-05-16T19:31:30.345916Z [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.98s
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 0x7fd2426dbb10>
test_client = <starlette.testclient.TestClient object at 0x7fd23d412be0>

    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.36s
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 0x7f415606fd90>

    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-05-16T19:55:53.885864Z [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.02s
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 0x7fc45a0c7d90>

    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-05-16T19:46:02.275836Z [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.00s
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 0x7f02f625fed0>
test_client = <starlette.testclient.TestClient object at 0x7f02f1c07bd0>

    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-05-16T19:23:12.034143Z [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.03s
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-05-16T19:26:40.762077Z [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 0x7f84efb74190>

    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 0x7f84f7162f90>
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-05-16T20:09:26.268042Z [info     ] HTTP Request: GET http://testserver/ "HTTP/1.1 307 Temporary Redirect"
2026-05-16T20:09:26.270425Z [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.92s
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 0x7f4e76ba7770>

    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-05-16T19:50:17.664244Z [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 0x7f036addfd90>

    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-05-16T20:09:09.491992Z [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.04s
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.62s
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 0x7f989d5e3120>
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 0x7f98a4c66f90>, 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 0x7f989d5ba690>

    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 0x7f989d5e3120>
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 0x7fb896b847e0>
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='140430779320032'>
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 0x7fb896ccfdf0>

    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: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)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

request = <starlette.requests.Request object at 0x7fb896b847e0>
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.26s
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 0x7fed61354ec0>
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='140657448792624'>
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 0x7fed61b6f950>
test_client = <starlette.testclient.TestClient object at 0x7fed5d50bac0>

    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 0x7fed61354ec0>
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.60s
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 0x7fd57ca82660>
scope = {'app': <fastapi.applications.FastAPI object at 0x7fd57ccb30e0>, '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 0x7fd57c91dd20>
send = <function ServerErrorMiddleware.__call__.<locals>._send at 0x7fd57c91e820>

    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.14s
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.65s
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.62s
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 0x7fdc2b757c50>

    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-05-16T20:02:12.168567Z [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.97s
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 0x7fd029833c50>

    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-05-16T19:23:28.698166Z [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 1.00s
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 0x7efc4c5da8d0>

    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-05-16T19:40:31.939463Z [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.01s
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 0x7fa1e4dc28d0>

    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-05-16T20:04:07.329701Z [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.02s
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 0x7f4d48eb1040>

    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-05-16T19:53:44.532613Z [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.87s
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 0x7fe1061a1040>

    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-05-16T19:24:32.549975Z [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.83s
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 0x7fb6d71bb020>

    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-05-16T19:46:19.720278Z [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.86s
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 0x7fb2852c3020>

    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-05-16T19:24:10.796605Z [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.85s
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 0x7f5e211bb8a0>

    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-05-16T19:34:30.837989Z [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 0x7f259fc9b8a0>

    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-05-16T19:25:52.743314Z [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.87s
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 0x7f0dd54b7770>

    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-05-16T20:07:27.884716Z [warning  ] Invalid webhook signature      _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=warning client_ip=testclient
2026-05-16T20:07:27.885695Z [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-05-16T20:07:27.884716Z'}
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.87s
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 0x7fc16d9a3770>

    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-05-16T19:26:11.413999Z [warning  ] Invalid webhook signature      _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=warning client_ip=testclient
2026-05-16T19:26:11.414913Z [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-05-16T19:26:11.413999Z'}
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/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 0x7f1fedd92690>

    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-05-16T19:49:22.046764Z [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-05-16T19:49:22.047585Z [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-05-16T19:49:22.046764Z'}
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 0x7f92fb0d6690>

    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-05-16T19:23:50.996323Z [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-05-16T19:23:50.997146Z [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-05-16T19:23:50.996323Z'}
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.88s
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 0x7fa6741bbdf0>

    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-05-16T19:30:13.121116Z [error    ] Error handling webhook event: Unexpected error _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=error client_ip=testclient extra={'error': 'Unexpected error'}
2026-05-16T19:30:13.121978Z [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-05-16T19:30:13.121116Z'}
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.87s
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 0x7f5feaf93df0>

    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-05-16T19:37:23.613348Z [error    ] Error handling webhook event: Unexpected error _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=error client_ip=testclient extra={'error': 'Unexpected error'}
2026-05-16T19:37:23.614144Z [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-05-16T19:37:23.613348Z'}
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.89s
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 0x7f8931ff4190>

    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-05-16T19:38:43.033843Z [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.06s
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 0x7f63ed7f4190>

    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-05-16T19:56:46.025946Z [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.03s
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 0x7f2159bc3b10>
test_client = <starlette.testclient.TestClient object at 0x7f2154902be0>

    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-05-16T20:02:03.526306Z [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.98s
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 0x7fbed25bbb10>
test_client = <starlette.testclient.TestClient object at 0x7fbecd316be0>

    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-05-16T19:33:18.920004Z [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 0x7ff923bf42d0>

    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-05-16T19:48:57.045628Z [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.05s
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 0x7f4bcb6202d0>

    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-05-16T19:36:48.265093Z [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.03s
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 0x7feaa28c7950>
test_client = <starlette.testclient.TestClient object at 0x7fea9e227ac0>

    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-05-16T19:47:30.143557Z [error    ] Failed to exchange code for credentials: API error _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=error client_ip=testclient
2026-05-16T19:47:30.144408Z [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-05-16T19:47:30.143557Z'}
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.07s
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 0x7f2600db3950>
test_client = <starlette.testclient.TestClient object at 0x7f25fc71fac0>

    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-05-16T19:53:13.543965Z [error    ] Failed to exchange code for credentials: API error _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=error client_ip=testclient
2026-05-16T19:53:13.544833Z [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-05-16T19:53:13.543965Z'}
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.05s
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.61s
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 0x7f007959c190>

    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-05-16T19:48:30.421105Z [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-05-16T19:43:54.206810Z [info     ] OpenTelemetry disabled         _logger=<_FixedFindCallerLogger stampbot.telemetry (INFO)> _name=info
2026-05-16T19:43:54.225515Z [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-05-16T19:43:54.225818Z [info     ] GitHub App credentials configured successfully _logger=<_FixedFindCallerLogger stampbot.main (INFO)> _name=info
2026-05-16T19:43:54.227828Z [info     ] HTTP Request: GET http://testserver/health "HTTP/1.1 404 Not Found"
2026-05-16T19:43:54.228290Z [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.83s
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 0x7f496bcb56e0>

    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-05-16T20:04:09.322619Z [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.86s
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 0x7f72199b3770>

    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-05-16T19:39:02.304680Z [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 0x7feb6ec83b10>
test_client = <starlette.testclient.TestClient object at 0x7feb69b1abe0>

    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-05-16T19:28:22.837923Z [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.96s
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 0x7f19566cbed0>
test_client = <starlette.testclient.TestClient object at 0x7f19520239b0>

    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-05-16T19:38:03.170690Z [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.01s
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 0x7f37dfd3f230>
test_client = <starlette.testclient.TestClient object at 0x7f37daa3e8b0>

    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-05-16T19:53:00.089866Z [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 1.01s
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.64s
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.61s
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.63s
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.60s
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.65s
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.67s
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.63s
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.61s
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.61s
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.61s
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.62s
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.61s
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.64s
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.65s
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.64s
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.61s
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.67s
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.61s
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.60s
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.59s
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.61s
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.66s
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.61s
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.61s
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.61s
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.65s
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.63s
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.63s
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.61s
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.62s
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.61s
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.63s
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.67s
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.63s
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.60s
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.61s
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.67s
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.68s
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.60s
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.61s
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.65s
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.60s
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.61s
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.62s
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.66s
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.61s
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.61s
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.60s
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.63s
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.62s
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.61s
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.66s
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.64s
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.63s
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 0x7ffa9b751550>, 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/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 0x7fc64979da90> is None

tests/test_telemetry.py:104: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-05-16T20:03:09.956116Z [warning  ] Invalid type MagicMock for attribute 'service.name' value. Expected one of ['bool', 'str', 'bytes', 'int', 'float'] or a sequence of those types
2026-05-16T20:03:09.979652Z [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-05-16T20:03:09.979652Z'}
=========================== short test summary info ============================
FAILED tests/test_telemetry.py::test_configure_telemetry_no_endpoint - assert <opentelemetry.sdk.trace.TracerProvider object at 0x7fc64979da90> is None
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 152 passed, 3 deselected in 1.10s
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.04s
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 0x7f2526d21550>, 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.84s
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 0x7f3d3fdbda90> is None

tests/test_telemetry.py:104: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-05-16T19:26:28.806409Z [warning  ] Invalid type MagicMock for attribute 'service.name' value. Expected one of ['bool', 'str', 'bytes', 'int', 'float'] or a sequence of those types
2026-05-16T19:26:28.829338Z [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-05-16T19:26:28.829338Z'}
=========================== short test summary info ============================
FAILED tests/test_telemetry.py::test_configure_telemetry_no_endpoint - assert <opentelemetry.sdk.trace.TracerProvider object at 0x7f3d3fdbda90> 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='140059649473376'>
args = (<Mock id='140059649473040'>,), 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.09s
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.03s
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.12s
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='140241814366464'>
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 0x7facc27d5160>
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='140379955150928'>
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 0x7facc5584190>

    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 0x7facc5584190>

    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.85s
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 0x7fc2db13c050>

    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.77s
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 0x7fbe22b5c550>

    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-05-16 20:03:14 [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.75s
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.64s
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='140256745138432'>
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.13s
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='140152859706096'>
args = ({'service.name': <MagicMock name='settings.otel_service_name' id='140152859707440'>, '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.20s
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='140491269585088'>

    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='140491269585760'>
args = (<Mock id='140491269585088'>,), 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='140491269585088'>

    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.16s
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.11s
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 0x7fca97dbe9c0>

    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-05-16T19:35:00.206506Z [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-05-16T19:35:00.207336Z [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-05-16T19:35:00.206506Z'}
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/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='139977184320144'>.call_count
E            +    where <Mock name='mock.set_attribute' id='139977184320144'> = <Mock id='139977184322832'>.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='139977184320144'>.call_count
 +    where <Mock name='mock.set_attribute' id='139977184320144'> = <Mock id='139977184322832'>.set_attribute
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 159 passed, 3 deselected in 1.06s
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='140021004412176'>.call_count
E        +    where <Mock name='mock.set_attribute' id='140021004412176'> = <Mock id='140021004412512'>.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='140021004412176'>.call_count
 +    where <Mock name='mock.set_attribute' id='140021004412176'> = <Mock id='140021004412512'>.set_attribute
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 149 passed, 3 deselected in 1.04s
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 0x7f20fc8439d0>

    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 0x7f20faa68c20>
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.70s
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.61s
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 0x7f5a9978b9d0>

    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 0x7f5a97b54c20>
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.66s
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 0x7fc3c86a39d0>

    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 0x7fc3c6754c20>
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.66s
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 0x7efd22adb9d0>

    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 0x7efd20d44c20>
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.67s
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 0x7fb2ff0139d0>

    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 0x7fb2fd358c20>
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.65s
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 0x7faab2f6f9d0>

    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 0x7faab1254c20>
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 0x7f518f5539d0>

    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 0x7f518d758c20>
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.69s
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 0x7f439b97b9d0>

    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 0x7f4399c44c20>
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 0x7fd4ca8479d0>

    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 0x7fd4c8b54c20>
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.68s
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 0x7fd0ab99f9d0>

    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 0x7fd0a9d50c20>
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 0x7ff1e21479d0>

    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 0x7ff1e0440c20>
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.66s
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 0x7fbbdf5cb9d0>

    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 0x7fbbdd858c20>
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.69s
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 0x7fd3540e79d0>

    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 0x7fd352354c20>
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.70s
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.69s
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 0x7f58b4b5f9d0>

    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 0x7f58b2e60c20>
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.68s
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.63s
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.62s
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.60s
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.61s
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.60s
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.60s
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.61s
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.66s
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.64s
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.64s
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.61s
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.63s
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.61s
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.60s
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.61s
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.63s
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.61s
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.60s
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.63s
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.61s
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.63s
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.61s
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.60s
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.62s
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.63s
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.62s
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.68s
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.61s
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.63s
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.63s
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.61s
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 0x7efe39f039d0>

    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 0x7efe383dd5b0>
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.61s
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 0x7f07cbee79d0>

    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 0x7f07ca2dd5b0>
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.68s
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 0x7fcc07ebb9d0>

    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 0x7fcc062dd5b0>
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.66s
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 0x7fe22048f9d0>

    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 0x7fe21e7dd5b0>
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.68s
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 0x7f2e8848f9d0>

    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 0x7f2e868ed5b0>
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 0x7f4163de39d0>

    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 0x7f41622f15b0>
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.67s
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 0x7fb49f16f9d0>

    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 0x7fb49d5f15b0>
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.65s
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 0x7f206f3c79d0>

    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 0x7f206d7dd5b0>
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.67s
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 0x7fa8f3f439d0>

    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 0x7fa8f23f15b0>
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 0x7fe485bdf9d0>

    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 0x7fe4840f15b0>
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 0x7fe9db45b9d0>

    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 0x7fe9d98f15b0>
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.70s
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 0x7feb1ec2b9d0>

    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 0x7feb1d0f15b0>
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: 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.65s
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.63s
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.64s
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 0x7f85d7f039d0>

    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 0x7f85d62f1810>
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.60s
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 0x7f530be679d0>

    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 0x7f530a2ed810>
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.68s
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 0x7f1ecf0c79d0>

    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 0x7f1ecd4dd810>
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: 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 0x7f49c48579d0>

    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 0x7f49c2cf1810>
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 0x7ff5f39279d0>

    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 0x7ff5f1bdd810>
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.62s
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 0x7f3f817239d0>

    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 0x7f3f7faed810>
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.61s
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.61s
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.60s
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.61s
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.61s
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.61s
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 0x7fd4c6aef9d0>

    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 0x7fd4c4d2d370>
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.66s
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.59s
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 0x7ff3fa6e79d0>

    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 0x7ff3f8925370>
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 0x7f98f94379d0>

    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 0x7f98f772d370>
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.66s
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 0x7f1c4711f9d0>

    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 0x7f1c4533d370>
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 0x7f3ec78d79d0>

    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 0x7f3ec5c11370>
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 0x7fe121d4b9d0>

    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 0x7fe12001d370>
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.63s
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.62s
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.60s
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.65s
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 0x7f450662f9d0>

    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 0x7f4504949150>
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 0x7f10142439d0>

    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 0x7f1012535150>
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.70s
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 0x7fb67d9af9d0>

    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 0x7fb67bc39150>
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 0x7fb547c479d0>

    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 0x7fb545f49150>
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.67s
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 0x7fe64d8eb9d0>

    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 0x7fe64bb4d150>
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.66s
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 0x7f38fb9b39d0>

    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 0x7f38f9c45150>
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 0x7f5ee53ef9d0>

    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 0x7f5ee3655150>
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.68s
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 0x7fbe100c39d0>

    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 0x7fbe0e345150>
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.65s
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.61s
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.61s
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.64s
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.61s
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.61s
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.60s
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.62s
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.60s
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.62s
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.60s
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.60s
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.70s
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.61s
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.62s
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 0x7ffae49f0a50>, <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 0x7ffae49f0a50>, <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.80s
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 0x7f6a172f8a50>, <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 0x7f6a172f8a50>, <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.76s
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 0x7fb897bf8cd0>, <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 0x7fb897bf8cd0>, <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_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 0x7ff319100a50>, <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 0x7ff319100a50>, <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_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 0x7f5d8e6eca50>, <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 0x7f5d8e6eca50>, <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 0x7fadb41d4cd0>, <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 0x7fadb41d4cd0>, <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 0x7fdebf7f8a50>, <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 0x7fdebf7f8a50>, <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.80s
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 0x7f1134bf0cd0>, <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 0x7f1134bf0cd0>, <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.77s
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.61s
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.60s
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 0x7f4ec99e4a50>, <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 0x7f4ec99e4a50>, <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.80s
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 0x7ff25a9f4cd0>, <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 0x7ff25a9f4cd0>, <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 0x7f0378a609d0>, <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 0x7f0378a609d0>, <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 0x7fd909f549d0>, <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 0x7fd909f549d0>, <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.76s
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.73s
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 0x7f7da92f8a50>, <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 0x7f7da92f8a50>, <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.77s
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 0x7f2a315f4cd0>, <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 0x7f2a315f4cd0>, <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.76s
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 0x7f3ba4400a50>, <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 0x7f3ba4400a50>, <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 0x7fe1dadd8cd0>, <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 0x7fe1dadd8cd0>, <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 0x7f5f40b509d0>, <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 0x7f5f40b509d0>, <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.79s
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 0x7f31cf7f4a50>, <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 0x7f31cf7f4a50>, <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 0x7f91565f0cd0>, <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 0x7f91565f0cd0>, <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='140676894991632'>
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.60s
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.67s
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.61s
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='140572520801552'>
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 0x7fd9972e43b0>
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.88s
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.60s
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.62s
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.63s
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.61s
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.61s
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.60s
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.62s
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.65s
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.63s
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.60s
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.61s
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.70s
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.70s
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.61s
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.62s
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.66s
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.61s
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.62s
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.60s
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.60s
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.62s
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.64s
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.60s
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.61s
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.60s
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.70s
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.63s
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.67s
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.60s
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.61s
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.68s
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.64s
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.63s
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.68s
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.61s
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.60s
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.62s
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.64s
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.63s
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.60s
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.60s
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.67s
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.63s
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.63s
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.61s
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.60s
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.62s
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.61s
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.61s
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.62s
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.65s
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.65s
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.59s
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.63s
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.62s
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.60s
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.63s
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.61s
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.60s
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.60s
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.67s
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.69s
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.67s
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.65s
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.61s
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.61s
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.62s
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.62s
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.60s
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.61s
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.61s
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.64s
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.61s
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 0x7fbff0238050>

    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 0x7fbfec278750>
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.77s
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 0x7f4067cdd160>
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='139914577231952'>
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 0x7f406a990190>

    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 0x7f4067cdd160>
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 0x7f406a990190>

    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.85s
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 0x7f9c367d8550>

    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-05-16 19:51:47 [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.74s
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 0x7f7e5cc5ed00>
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='140180699478544'>
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 0x7f7e60c88690>

    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 0x7f7e5cc5ed00>
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.85s
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 0x7f54787c87d0>

    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-05-16 19:43:15 [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.74s
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 0x7efbfd2268f0>
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='139620747654352'>
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 0x7efc0154c910>

    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 0x7efbfd2268f0>
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.65s
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 0x7f1b1dad8a50>

    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-05-16 19:43:07 [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.75s
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 0x7f381b650cd0>
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='139878954813152'>
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 0x7f381fa55940>

    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 0x7f381b650cd0>
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.87s
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 0x7fecc3f74cd0>

    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-05-16 19:28:56 [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.75s
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 0x7f5b15a74f30>
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='140029182509808'>
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 0x7f5b19a64e10>

    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 0x7f5b15a74f30>
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 0x7f91cda856d0>

    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-05-16 19:43:36 [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.80s
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 0x7f1fdf6b6390>
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='139774868999200'>
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 0x7f1fe3c55810>

    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 0x7f1fdf6b6390>
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.92s
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 0x7f5adaf79450>

    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-05-16 19:44:53 [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 0x7f9363e31f70>
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='140271012571392'>
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 0x7f936c2a5590>

    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 0x7f9363e31f70>
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 0x7fd3fd9641d0>
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='140548469200544'>
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 0x7fd401ee2060>

    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 0x7fd3fd9641d0>
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.94s
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 0x7fea62e20f50>

    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-05-16 19:28:08 [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.76s
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 0x7f4cd6c342f0>
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='139967997316128'>
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 0x7f4cdb0f5a70>

    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 0x7f4cd6c342f0>
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 0x7f51e5e591d0>

    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-05-16 19:41:16 [debug    ] User alice is a member of team release-team extra={'org': 'acme', 'username': 'alice', 'team': 'release-team'}
2026-05-16 19:41:16 [debug    ] User alice is a member of team deploy-team extra={'org': 'acme', 'username': 'alice', 'team': 'deploy-team'}
2026-05-16 19:41:16 [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.78s
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 0x7fd14df13110>
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='140536934365376'>
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 0x7fd1526845f0>

    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 0x7fd14df13110>
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.92s
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 0x7f6cdf2b4050>

    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 0x7f6cdaf98750>
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_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 0x7f7b2f4bd160>
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='140167032275024'>
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 0x7f7b3266c190>

    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 0x7f7b2f4bd160>
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 0x7f7b3266c190>

    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 0x7f2fbb15c550>

    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-05-16 19:25:48 [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_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 0x7f039e06ed00>
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='139653513296400'>
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 0x7f03a23c4690>

    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 0x7f039e06ed00>
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_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 0x7f6852b807d0>

    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-05-16 19:52: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.74s
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 0x7fe2c3d228f0>
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='140611923522768'>
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 0x7fe2c80a4910>

    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 0x7fe2c3d228f0>
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.87s
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.62s
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 0x7f022c284a50>

    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-05-16 19:43:46 [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.76s
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 0x7f8b8a158d50>
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='140237294159584'>
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 0x7f8b8e485940>

    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 0x7f8b8a158d50>
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_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 0x7f03544c0cd0>

    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-05-16 19:51:34 [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 0x7fc1af140f30>
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='140469843215088'>
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 0x7fc1b3270e10>

    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 0x7fc1af140f30>
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 0x7f1f86c916d0>

    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-05-16 19:42:57 [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 0x7f91697ae570>
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='140262516575264'>
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 0x7f916de89810>

    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 0x7f91697ae570>
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_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 0x7f56f237d450>

    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-05-16 20:07:01 [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.82s
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 0x7f8a53e31bb0>
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='140232089430272'>
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 0x7f8a5c1e9590>

    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 0x7f8a53e31bb0>
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.93s
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 0x7fc2d9b97950>
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='140474853086880'>
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 0x7fc2dde9e060>

    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 0x7fc2d9b97950>
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_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 0x7f7fc38ecf50>

    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-05-16 19:46:30 [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_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 0x7f7472234b30>
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='140138107800608'>
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 0x7f7476705a70>

    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 0x7f7472234b30>
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 0x7efd6649d1d0>

    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-05-16 20:07:16 [debug    ] User alice is a member of team release-team extra={'org': 'acme', 'username': 'alice', 'team': 'release-team'}
2026-05-16 20:07:16 [debug    ] User alice is a member of team deploy-team extra={'org': 'acme', 'username': 'alice', 'team': 'deploy-team'}
2026-05-16 20:07:16 [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.80s
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 0x7f651a9e6e10>
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='140072214755520'>
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 0x7f651ee745f0>

    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 0x7f651a9e6e10>
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_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 0x7f35758c8050>

    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 0x7f3571888750>
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 0x7f095d9d5160>
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='139678183080016'>
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 0x7f0960b80190>

    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 0x7f095d9d5160>
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 0x7f0960b80190>

    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_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 0x7f4bb8c0c550>

    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-05-16 19:52: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.77s
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 0x7fdfd9262d00>
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='140599397988880'>
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 0x7fdfdd164690>

    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 0x7fdfd9262d00>
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_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 0x7f4ff1c147d0>

    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-05-16 19:37:50 [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 0x7f33f95f28f0>
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='139861202695376'>
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 0x7f33fd434910>

    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 0x7f33f95f28f0>
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.87s
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.67s
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 0x7f4daf98ca50>

    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-05-16 20:05:52 [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.76s
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 0x7f3480d58cd0>
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='139863476815584'>
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 0x7f3485121940>

    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 0x7f3480d58cd0>
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 0x7f075f194cd0>

    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-05-16 19:56:31 [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.78s
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 0x7f9a0a850f30>
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='140299578671856'>
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 0x7f9a0ed08e10>

    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 0x7f9a0a850f30>
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_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 0x7f7475e9d6d0>

    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-05-16 19:28:35 [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 0x7fce76eb73b0>
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='140524735056928'>
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 0x7fce7b0dd810>

    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 0x7fce76eb73b0>
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_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 0x7f965d735450>

    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-05-16 19:23:36 [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 0x7f0073545e50>
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='139639911459072'>
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 0x7f007780d590>

    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 0x7f0073545e50>
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_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 0x7f511d1878f0>
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='139986357114528'>
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 0x7f512171e060>

    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 0x7f511d1878f0>
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.92s
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 0x7fd5f6f6cf50>

    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-05-16 19:41:56 [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 0x7f704f958110>
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='140120348216352'>
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 0x7f7053c39a70>

    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 0x7f704f958110>
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.87s
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 0x7f6eed4d91d0>

    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-05-16 19:42:20 [debug    ] User alice is a member of team release-team extra={'org': 'acme', 'username': 'alice', 'team': 'release-team'}
2026-05-16 19:42:20 [debug    ] User alice is a member of team deploy-team extra={'org': 'acme', 'username': 'alice', 'team': 'deploy-team'}
2026-05-16 19:42:20 [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_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 0x7f1c701219d0>
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='139760117889216'>
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 0x7f1c747105f0>

    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 0x7f1c701219d0>
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_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 0x7fdb33194050>

    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 0x7fdb2f2a0750>
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.75s
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 0x7fe4a39c5160>
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='140619955273808'>
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 0x7fe4a65cc190>

    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 0x7fe4a39c5160>
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 0x7fe4a65cc190>

    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 0x7f9334b7c550>

    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-05-16 20:08:23 [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.78s
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 0x7fc75976ed00>
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='140494176582160'>
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 0x7fc75d65c690>

    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 0x7fc75976ed00>
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 0x7f6e4269c7d0>

    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-05-16 19:33:51 [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 0x7fd551f228f0>
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='140554178442448'>
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 0x7fd556364910>

    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 0x7fd551f228f0>
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_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.65s
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 0x7f1f9632ca50>

    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-05-16 19:50:35 [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 0x7f3a72c2ccd0>
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='139889010525920'>
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 0x7f3a76f8d940>

    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 0x7f3a72c2ccd0>
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_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 0x7fdaa74e8cd0>

    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-05-16 19:36:26 [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 0x7fc2a256cf30>
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='140473924453104'>
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 0x7fc2a6590e10>

    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 0x7fc2a256cf30>
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 0x7f4d141696d0>

    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-05-16 19:54:09 [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.83s
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 0x7f9145fc65d0>
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='140261921082400'>
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 0x7f914a189810>

    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 0x7f9145fc65d0>
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.91s
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 0x7f0abf16d450>

    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-05-16 19:31:49 [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_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 0x7fcb0f049730>
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='140510106872064'>
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 0x7fcb13761590>

    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 0x7fcb0f049730>
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_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 0x7f67e7486810>
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='140084238538400'>
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 0x7f67eba02060>

    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 0x7f67e7486810>
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_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 0x7f53b4cf0f50>

    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-05-16 20:04:25 [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_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 0x7fcbb6b47e30>
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='140512920325152'>
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 0x7fcbbb019a70>

    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 0x7fcbb6b47e30>
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.91s
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 0x7f930d28d1d0>

    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-05-16 20:01:03 [debug    ] User alice is a member of team release-team extra={'org': 'acme', 'username': 'alice', 'team': 'release-team'}
2026-05-16 20:01:03 [debug    ] User alice is a member of team deploy-team extra={'org': 'acme', 'username': 'alice', 'team': 'deploy-team'}
2026-05-16 20:01:03 [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_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 0x7fa10df15b50>
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='140329702209728'>
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 0x7fa1123445f0>

    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 0x7fa10df15b50>
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 0x7ff8c4114050>

    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 0x7ff8c0188750>
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 0x7f62348d5160>
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='140059746238544'>
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 0x7f6237554190>

    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 0x7f62348d5160>
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 0x7f6237554190>

    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 0x7f28c42d4550>

    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-05-16 20:08:19 [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.78s
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 0x7efbfb466d00>
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='139620718000656'>
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 0x7efbff33c690>

    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 0x7efbfb466d00>
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_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 0x7f46940747d0>

    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-05-16 19:33: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.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 0x7f899dc168f0>
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='140229032817872'>
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 0x7f89a1cc4910>

    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 0x7f899dc168f0>
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.87s
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.63s
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 0x7f8a65824a50>

    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-05-16 19:58:43 [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 0x7f3952c68cd0>
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='139884178917088'>
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 0x7f3956fe9940>

    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 0x7f3952c68cd0>
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_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 0x7fae1dd14cd0>

    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-05-16 19:57:38 [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 0x7f827dd64f30>
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='140198434161392'>
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 0x7f8281d8ce10>

    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 0x7f827dd64f30>
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_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 0x7f6db92d56d0>

    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-05-16 19:46:44 [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_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 0x7fb28bebdd90>
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='140404828326944'>
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 0x7fb290425810>

    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 0x7fb28bebdd90>
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.91s
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 0x7faff02cd450>

    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-05-16 19:30:45 [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_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 0x7fcd30444e90>
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='140519254632704'>
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 0x7fcd3461d590>

    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 0x7fcd30444e90>
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.93s
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 0x7f11db18ef90>
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='139714666911392'>
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 0x7f11df502060>

    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 0x7f11db18ef90>
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_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 0x7f9ee44dcf50>

    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-05-16 20:10:13 [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.80s
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 0x7ff96cf43fb0>
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='140709251484704'>
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 0x7ff9713c9a70>

    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 0x7ff96cf43fb0>
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.93s
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 0x7f2e2d61d1d0>

    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-05-16 19:55:43 [debug    ] User alice is a member of team release-team extra={'org': 'acme', 'username': 'alice', 'team': 'release-team'}
2026-05-16 19:55:43 [debug    ] User alice is a member of team deploy-team extra={'org': 'acme', 'username': 'alice', 'team': 'deploy-team'}
2026-05-16 19:55:43 [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_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 0x7f651d007050>
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='140072256813248'>
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 0x7f65217605f0>

    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 0x7f651d007050>
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_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 0x7fe7a58e4050>

    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 0x7fe7a1aa0750>
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.80s
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 0x7facf7cb9160>
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='140380849487952'>
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 0x7facfaa4c190>

    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 0x7facf7cb9160>
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 0x7facfaa4c190>

    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_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 0x7fca603ac550>

    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-05-16 20:10:15 [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.76s
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 0x7f3edc656d00>
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='139907962731024'>
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 0x7f3ee0518690>

    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 0x7f3edc656d00>
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 0x7fc358e0c7d0>

    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-05-16 19:49:53 [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 0x7f994a6268f0>
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='140296353559760'>
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 0x7f994e57c910>

    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 0x7f994a6268f0>
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.85s
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.65s
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 0x7f4af6384a50>

    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-05-16 20:07:55 [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 0x7f01d4458cd0>
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='139645833332448'>
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 0x7f01d8811940>

    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 0x7f01d4458cd0>
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_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 0x7f18a7e2ccd0>

    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-05-16 19:47:32 [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.77s
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 0x7f7cf5154f30>
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='140174664975088'>
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 0x7f7cf9204e10>

    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 0x7f7cf5154f30>
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_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 0x7ff5310856d0>

    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-05-16 19:36:28 [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 0x7fb61e1c1790>
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='140420165864480'>
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 0x7fb62234d810>

    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 0x7fb61e1c1790>
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.89s
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 0x7f893fc69450>

    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-05-16 19:48:45 [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_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 0x7fcc04348a10>
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='140514220435712'>
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 0x7fcc08559590>

    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 0x7fcc04348a10>
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 0x7f597beaecf0>
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='140022307706528'>
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 0x7f5980282060>

    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 0x7f597beaecf0>
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.94s
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 0x7f3a93e24f50>

    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-05-16 19:37:25 [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_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 0x7fb0274482f0>
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='140394549726240'>
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 0x7fb02b8c9a70>

    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 0x7fb0274482f0>
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.90s
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 0x7f0310f911d0>

    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-05-16 20:06:57 [debug    ] User alice is a member of team release-team extra={'org': 'acme', 'username': 'alice', 'team': 'release-team'}
2026-05-16 20:06:57 [debug    ] User alice is a member of team deploy-team extra={'org': 'acme', 'username': 'alice', 'team': 'deploy-team'}
2026-05-16 20:06:57 [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.83s
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 0x7ffb9e30dd90>
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='140718669347008'>
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 0x7ffba29f85f0>

    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 0x7ffb9e30dd90>
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_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.61s
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.69s
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.61s
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.61s
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.61s
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.61s
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.61s
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.61s
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.63s
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.61s
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.62s
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.62s
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.66s
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.62s
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.68s
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.61s
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.60s
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.69s
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.61s
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.62s
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 0x7f4a7ba80cd0>

    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.77s
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 0x7f1621e8ccd0>

    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_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 0x7f3cc3a9d590>

    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-05-16 20:08:59 [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.82s
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 0x7fc4d6d58cd0>

    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.75s
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 0x7fcedbf94cd0>

    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 0x7f0b71195590>

    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-05-16 19:57:20 [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 0x7f49ce794cd0>

    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.74s
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.61s
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.61s
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 0x7f3f6c00ccd0>

    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_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 0x7f7800888cd0>

    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 0x7f7f45479590>

    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-05-16 19:44:57 [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.60s
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.65s
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 0x7f70dbc85f30>

    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.78s
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 0x7f0701e68cd0>

    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.80s
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 0x7f06d87f4cd0>

    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 0x7fce1c61ccd0>

    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.76s
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.61s
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 0x7f5731e04f50>

    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_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 0x7f8c07940f50>

    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.74s
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 0x7f0522e3d090>

    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.76s
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 0x7f6f4f1c8f50>

    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.60s
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 0x7f9e0fb8cf50>

    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.74s
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 0x7f601d884c30>

    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 0x7f6019639d30>, app_id = None
private_key = <MagicMock name='settings.private_key' id='140050719543648'>

    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/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 0x7f38899ecb00>

    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 0x7f3886877bf0>

    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 0x7f62d0590050>

    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 0x7f62cc6a0750>

    @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.75s
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.63s
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.63s
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 0x7f0f54f75310>

    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='139703758660208'>
args = ('release-team',), kwargs = {}, expected = call('release-team')
actual = call('acme')
_error_message = <function NonCallableMock.assert_called_with.<locals>._error_message at 0x7f0f50fecbf0>
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-05-16 19:27:49 [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.60s
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 0x7faa7f70d310>

    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='140370191753840'>
args = ('release-team',), kwargs = {}, expected = call('release-team')
actual = call('acme')
_error_message = <function NonCallableMock.assert_called_with.<locals>._error_message at 0x7faa7b610bf0>
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-05-16 19:37:06 [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.80s
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.63s
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.62s
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 0x7f48a5fdfc50>

    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.77s
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 0x7fa30a8916e0>
tmp_path = PosixPath('/tmp/pytest-of-runner/pytest-940/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 0x7fa307992250>

    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-940/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-940/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 0x7fa746294b00>

    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 0x7fa74338fbf0>

    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 0x7f454be57c50>

    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.72s
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 0x7fa2766c3c50>

    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.73s
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 0x7fc93a7a4c30>

    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 0x7fc936535d30>, app_id = None
private_key = <MagicMock name='settings.private_key' id='140502176584032'>

    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.75s
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 0x7fa470ed8050>

    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 0x7fa46d0a0750>

    @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.77s
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 0x7fc06a300b00>

    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 0x7fc06738bbf0>

    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 0x7f6487bc4b00>

    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 0x7f6483a67bf0>

    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.75s
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 0x7f42236856e0>
tmp_path = PosixPath('/tmp/pytest-of-runner/pytest-865/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 0x7f42206ae250>

    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-865/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-865/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 0x7f40c671cb00>

    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 0x7f40c356fbf0>

    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.77s
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 0x7f32a818ca50>

    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.73s
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 0x7f438400ccd0>

    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.75s
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 0x7f2689cbd590>

    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-05-16 20:02:30 [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 0x7f31859cd310>

    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='139850603307632'>
args = ('release-team',), kwargs = {}, expected = call('release-team')
actual = call('acme/release-team')
_error_message = <function NonCallableMock.assert_called_with.<locals>._error_message at 0x7f31819e4bf0>
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-05-16 19:28:02 [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.82s
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 0x7f56e3e851d0>

    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/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.64s
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 0x7f9a3bd88b00>

    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 0x7f9a37b5bbf0>._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 0x7f9a37b5bbf0>._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 0x7f63a1634550>

    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-05-16 19:33:38 [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 0x7fb8ee1407d0>

    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-05-16 19:44:06 [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.76s
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 0x7f58e04616d0>

    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-05-16 20:03:22 [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.80s
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.62s
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 0x7fc48c985450>

    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.82s
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 0x7f130f5979d0>

    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 0x7f130b44b390>._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 0x7f130b44b390>._initialized
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 35 passed, 3 deselected in 0.71s
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 0x7fb8f664c690>

    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-05-16 19:56:36 [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.75s
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 0x7fbc19f7c910>

    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-05-16 19:47:01 [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.77s
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 0x7f0b982ad810>

    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-05-16 19:50:46 [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.78s
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.63s
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 0x7f24b3339590>

    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.76s
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 0x7fcdc327da70>

    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-05-16 20:08:03 [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.82s
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 0x7f26b9da8cd0>

    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 0x7f0567f59810>

    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 0x7f0563dde3f0>

    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-05-16 19:29:52 [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.66s
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.61s
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 0x7f4b0fc03f20>

    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='139960363868448'>
args = (PosixPath('/tmp/pytest-of-runner/pytest-394/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 0x7f4b13d40710>
tmp_path = PosixPath('/tmp/pytest-of-runner/pytest-394/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 0x7f4b0fc03f20>

    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.84s
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 0x7f9b880b5160>
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='140305960190032'>
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 0x7f9b8ae44190>

    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 0x7f9b880b5160>
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 0x7f9b8ae44190>

    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 0x7f2d9f648b90>
client = <Mock id='139833924946976'>, 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='139833924947312'>, 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 0x7f2da38c4410>

    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 0x7f2d9f648b90>
client = <Mock id='139833924946976'>, 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 0x7f4bf1066d00>
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='139964143417872'>
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 0x7f4bf4f80690>

    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 0x7f4bf1066d00>
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.85s
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 0x7fa1cda0a8f0>
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='140332917306576'>
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 0x7fa1d1a34910>

    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 0x7fa1cda0a8f0>
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.86s
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 0x7f66f7154dd0>
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='140080209069792'>
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 0x7f66fb55d940>

    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 0x7f66f7154dd0>
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.90s
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 0x7f66b5664f30>
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='140079107244784'>
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 0x7f66b9a88e10>

    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 0x7f66b5664f30>
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.87s
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 0x7fb7bd9ba5d0>
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='140427136764960'>
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 0x7fb7c1b65810>

    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 0x7fb7bd9ba5d0>
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.90s
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 0x7f4215f41e50>
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='139921812711680'>
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 0x7f421a2a1590>

    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 0x7f4215f41e50>
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.93s
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 0x7f3614363b30>
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='139870243952288'>
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 0x7f36188fa060>

    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 0x7f3614363b30>
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.91s
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.61s
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 0x7fe73422be90>
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='140630988819488'>
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 0x7fe7386eda70>

    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 0x7fe73422be90>
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.87s
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.61s
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 0x7f4ef3d05c70>
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='139977076537536'>
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 0x7f4ef810c5f0>

    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 0x7f4ef3d05c70>
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.61s
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.67s
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.60s
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.63s
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.60s
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.61s
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.65s
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.62s
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.62s
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.60s
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 0x7f71eb1d9590>

    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-05-16 19:38: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.77s
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 0x7f7cc8e55590>

    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-05-16 19:31:45 [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.77s
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.64s
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.61s
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.60s
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.62s
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 0x7f7889479310>

    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='140155607949936'>
args = ('release-team',), kwargs = {}, expected = call('release-team')
actual = call('acme')
_error_message = <function NonCallableMock.assert_called_with.<locals>._error_message at 0x7f7885300bf0>
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-05-16 19:38:20 [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 0x7fe448db5310>

    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='140618382172784'>
args = ('release-team',), kwargs = {}, expected = call('release-team')
actual = call('acme')
_error_message = <function NonCallableMock.assert_called_with.<locals>._error_message at 0x7fe444ce0bf0>
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-05-16 19:24:21 [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 0x7f74d1997d90>

    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.72s
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 0x7f2ff8dd8cd0>

    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 0x7f73d28151d0>

    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.77s
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.13s
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.14s
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.62s
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.62s
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.60s
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.60s
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.61s
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.66s
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.69s
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.63s
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.60s
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.61s
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.60s
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.61s
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.61s
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.63s
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.66s
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.62s
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.60s
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.61s
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.64s
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.62s
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.67s
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.61s
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.65s
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.60s
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.60s
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.60s
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.62s
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.61s
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.62s
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.69s
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.61s
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.69s
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.63s
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.60s
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.61s
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.60s
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.61s
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.61s
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.60s
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.60s
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.63s
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.61s
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.62s
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.61s
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.64s
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.62s
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.65s
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.62s
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.61s
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.63s
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.62s
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.67s
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.61s
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.60s
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.61s
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.60s
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.61s
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.61s
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.47s
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.48s
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.64s
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 0x7f62aa764b50>.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 0x7f62aa764b50>.needs_team_check
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 32 passed, 3 deselected in 0.47s
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.45s
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.47s
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.45s
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.45s
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.47s
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 0x7f280738cb50>.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 0x7f280738cb50>.needs_team_check
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 32 passed, 3 deselected in 0.48s
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 0x7f82b2f9cd60>.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 0x7f82b2f9cd60>.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 0x7f75a8906660>.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 0x7f75a8906660>.approval_labels
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 3 deselected in 0.42s
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.42s
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.44s
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.48s
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 0x7f2a03504f70>.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 0x7f2a03504f70>.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.67s
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.61s
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 0x7f844c30d550>, 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.84s
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.44s
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.44s
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.46s
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.46s
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 0x7fe0e0cfcb50>.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 0x7fe0e0cfcb50>.needs_team_check
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 32 passed, 3 deselected in 0.49s
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 0x7fb4b7e54d60>.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 0x7fb4b7e54d60>.needs_team_check
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 33 passed, 3 deselected in 0.48s
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.69s
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.46s
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 0x7f989c80cf70>.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 0x7f989c80cf70>.needs_team_check
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 34 passed, 3 deselected in 0.45s
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.44s
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 0x7f0c07ef9220>.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 0x7f0c07ef9220>.required_title_patterns
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 12 passed, 3 deselected in 0.43s
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.46s
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 0x7fc301b59470>
state = <re._parser.State object at 0x7fc3016064d0>, 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.51s
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.45s
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.40s
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.43s
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.44s
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.43s
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.60s
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.64s
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.66s
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.60s
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.61s
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.60s
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.60s
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.67s
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.62s
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.65s
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 0x7fb3d4a91450>

    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.89s
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 0x7f994bf01450>

    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.91s
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.61s
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 0x7f7bac961450>

    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 0x7f6948459810>

    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.87s
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 0x7f7966d31450>

    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.90s
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 0x7f7dfcec9810>

    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.90s
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 0x7ff8a21cd810>

    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.87s
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 0x7fc61100d810>

    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_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 0x7f78a3f7e780>

    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 0x7f7e61171810>

    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_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 0x7fb294082780>

    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 0x7fb2358f5810>

    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.89s
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 0x7f21d876ed50>

    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 0x7f21d876ed50>

    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 0x7fef9c4a6d50>

    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 0x7fef9c4a6d50>

    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.93s
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 0x7f91f4b7f390>

    @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.91s
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 0x7f933e73b390>

    @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.90s
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 0x7f032d1f6d50>

    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 0x7f032d1f6d50>

    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/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 0x7fb5359c5450>

    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.89s
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 0x7f630b2f2d50>

    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.89s
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 0x7fa8dbf7d450>

    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 0x7f15b148f390>

    @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 0x7f4ce95da060>

    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.92s
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 0x7fb31bf9e580>

    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.88s
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.61s
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 0x7f3b46ad6d70>

    @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-05-16T19:24:34.519826Z [info     ] Exchanging manifest code for credentials _logger=<_FixedFindCallerLogger stampbot.manifest (INFO)> _name=info
2026-05-16T19:24:34.661614Z [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-05-16T19:24:34.519826Z'}
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.13s
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.62s
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.60s
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.60s
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 0x7fec8a3e7df0>

    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.88s
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 0x7f45e7ffbdf0>

    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.89s
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 0x7f223997bdf0>

    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/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 0x7efe811b3770>

    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 0x7efe811b6e40>
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.21s
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 0x7f70f0f5dff0>
mock_github_client = <MagicMock name='github_client' id='140123056205712'>

    @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 0x7f70f0f5dff0>
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-05-16T19:43:56.440930Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-05-16T19:43:56.441480Z [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-05-16T19:43:56.440930Z'}
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-05-16T19:43:56.441480Z'}
=========================== 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 0x7ff6526b7770>

    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 0x7ff6526bae40>
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.22s
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 0x7f21ac75dff0>
mock_github_client = <MagicMock name='github_client' id='139782604550032'>

    @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 0x7f21ac75dff0>
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-05-16T19:28:17.157365Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-05-16T19:28:17.157898Z [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-05-16T19:28:17.157365Z'}
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-05-16T19:28:17.157898Z'}
=========================== 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.16s
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 0x7ffbdd983770>

    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 0x7ffbdd986e40>
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.21s
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 0x7f0852267330>
mock_github_client = <MagicMock name='github_client' id='139673715208080'>

    @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 0x7f0852267330>
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-05-16T19:56:21.713947Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-05-16T19:56:21.714503Z [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-05-16T19:56:21.713947Z'}
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-05-16T19:56:21.714503Z'}
=========================== 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_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 0x7f699d097770>

    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 0x7f699d09ae40>
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 0x7f87a9267330>
mock_github_client = <MagicMock name='github_client' id='140220635672464'>

    @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 0x7f87a9267330>
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-05-16T19:40:40.335046Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-05-16T19:40:40.335628Z [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-05-16T19:40:40.335046Z'}
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-05-16T19:40:40.335628Z'}
=========================== 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 0x7fb07719b770>

    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 0x7fb0771a2e40>
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.20s
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 0x7f0d4a15b330>
mock_github_client = <MagicMock name='github_client' id='139695054729104'>

    @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 0x7f0d4a15b330>
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-05-16T19:42:53.263660Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-05-16T19:42:53.264184Z [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-05-16T19:42:53.263660Z'}
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-05-16T19:42:53.264184Z'}
=========================== 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_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 0x7f069429b770>

    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 0x7f069429ee40>
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.18s
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 0x7fe590c6dff0>
mock_github_client = <MagicMock name='github_client' id='140623953686416'>

    @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 0x7fe590c6dff0>
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-05-16T20:08:37.626863Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-05-16T20:08:37.627418Z [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-05-16T20:08:37.626863Z'}
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-05-16T20:08:37.627418Z'}
=========================== 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.20s
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 0x7f2477fab770>

    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 0x7f2477fb2e40>
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_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 0x7f646d1371d0>
mock_github_client = <MagicMock name='github_client' id='140069303938960'>

    @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 0x7f646d1371d0>
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-05-16T19:38:49.878350Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-05-16T19:38:49.878892Z [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-05-16T19:38:49.878350Z'}
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-05-16T19:38:49.878892Z'}
=========================== 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_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 0x7f7cb6dab770>

    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 0x7f7cb6daee40>
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.16s
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 0x7f80c1931ff0>
mock_github_client = <MagicMock name='github_client' id='140190980681616'>

    @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 0x7f80c1931ff0>
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-05-16T20:02:16.276814Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-05-16T20:02:16.277370Z [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-05-16T20:02:16.276814Z'}
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-05-16T20:02:16.277370Z'}
=========================== 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_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 0x7f62058a3770>

    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 0x7f62058a6e40>
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.16s
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 0x7ff4ae943330>
mock_github_client = <MagicMock name='github_client' id='140688878186384'>

    @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 0x7ff4ae943330>
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-05-16T19:31:52.160339Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-05-16T19:31:52.160875Z [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-05-16T19:31:52.160339Z'}
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-05-16T19:31:52.160875Z'}
=========================== 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 0x7f42a30a7770>

    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 0x7f42a30aae40>
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_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 0x7f9a52145ff0>
mock_github_client = <MagicMock name='github_client' id='140300779253648'>

    @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 0x7f9a52145ff0>
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-05-16T19:57:40.920950Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-05-16T19:57:40.921508Z [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-05-16T19:57:40.920950Z'}
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-05-16T19:57:40.921508Z'}
=========================== 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.15s
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 0x7f60711a7770>

    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 0x7f60711aae40>
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 0x7f979c265ff0>
mock_github_client = <MagicMock name='github_client' id='140289137078160'>

    @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 0x7f979c265ff0>
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-05-16T19:32:32.146857Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-05-16T19:32:32.147424Z [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-05-16T19:32:32.146857Z'}
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-05-16T19:32:32.147424Z'}
=========================== 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.60s
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.66s
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.62s
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.65s
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.61s
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.60s
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.66s
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.60s
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.61s
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.62s
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.61s
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.60s
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.60s
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.60s
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.61s
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.63s
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 0x7f7ccb317e10>
mock_github_client = <MagicMock name='github_client' id='140173960086256'>

    @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 0x7f7ccb317e10>
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-05-16T19:42:01.280286Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T19:42:01.280977Z [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-05-16T19:42:01.281852Z [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-05-16T19:42:01.280286Z'}
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-05-16T19:42:01.280977Z'}
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-05-16T19:42:01.281852Z'}
=========================== 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 0x7f6dc719d4f0>
mock_github_client = <MagicMock name='github_client' id='140109464120224'>

    @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-05-16T19:56:40.981622Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-05-16T19:56:40.982161Z [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-05-16T19:56:40.982697Z [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-05-16T19:56:40.983154Z [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-05-16T19:56:40.983402Z [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-05-16T19:56:40.981622Z'}
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-05-16T19:56:40.982161Z'}
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-05-16T19:56:40.982697Z'}
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-05-16T19:56:40.983154Z'}
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-05-16T19:56:40.983402Z'}
=========================== 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.12s
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 0x7f6120109450>
mock_github_client = <MagicMock name='github_client' id='140055194134112'>

    @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-05-16T20:06:27.560121Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-05-16T20:06:27.560702Z [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-05-16T20:06:27.561267Z [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-05-16T20:06:27.561995Z [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-05-16T20:06:27.560121Z'}
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-05-16T20:06:27.560702Z'}
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-05-16T20:06:27.561267Z'}
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-05-16T20:06:27.561995Z'}
=========================== 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.13s
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 0x7f9bf7077ef0>
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='140307835508720'>
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 0x7f9bf7077ef0>
mock_github_client = <MagicMock name='github_client' id='140307835507040'>

    @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 0x7f9bf7077ef0>
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-05-16T19:27:37.416636Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-05-16T19:27:37.417186Z [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-05-16T19:27:37.417727Z [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-05-16T19:27:37.416636Z'}
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-05-16T19:27:37.417186Z'}
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-05-16T19:27:37.417727Z'}
=========================== 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.81s
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 0x7f7d03ddfe10>
mock_github_client = <MagicMock name='github_client' id='140174908834544'>

    @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 0x7f7d03ddfe10>
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-05-16T19:58:28.506377Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T19:58:28.507007Z [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-05-16T19:58:28.507868Z [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-05-16T19:58:28.506377Z'}
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-05-16T19:58:28.507007Z'}
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-05-16T19:58:28.507868Z'}
=========================== 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_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 0x7fb6cf39d4f0>
mock_github_client = <MagicMock name='github_client' id='140423133047712'>

    @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-05-16T19:51:01.269909Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-05-16T19:51:01.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-05-16T19:51:01.270959Z [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-05-16T19:51:01.271384Z [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-05-16T19:51:01.271577Z [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-05-16T19:51:01.269909Z'}
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-05-16T19:51:01.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-05-16T19:51:01.270959Z'}
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-05-16T19:51:01.271384Z'}
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-05-16T19:51:01.271577Z'}
=========================== 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 0x7fecefbed550>
mock_github_client = <MagicMock name='github_client' id='140655673370208'>

    @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-05-16T19:58:18.152488Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-05-16T19:58:18.153015Z [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-05-16T19:58:18.153568Z [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-05-16T19:58:18.154256Z [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-05-16T19:58:18.152488Z'}
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-05-16T19:58:18.153015Z'}
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-05-16T19:58:18.153568Z'}
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-05-16T19:58:18.154256Z'}
=========================== 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/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 0x7faecd6901d0>
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='140388741568496'>
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 0x7faecd6901d0>
mock_github_client = <MagicMock name='github_client' id='140388741566816'>

    @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 0x7faecd6901d0>
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-05-16T20:00:31.736669Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-05-16T20:00:31.737249Z [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-05-16T20:00:31.737788Z [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-05-16T20:00:31.736669Z'}
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-05-16T20:00:31.737249Z'}
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-05-16T20:00:31.737788Z'}
=========================== 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 0x7fc45ce1be10>
mock_github_client = <MagicMock name='github_client' id='140481346994928'>

    @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 0x7fc45ce1be10>
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-05-16T19:37:58.854626Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T19:37:58.855326Z [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-05-16T19:37:58.856158Z [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-05-16T19:37:58.854626Z'}
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-05-16T19:37:58.855326Z'}
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-05-16T19:37:58.856158Z'}
=========================== 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_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 0x7f9a29fcd4f0>
mock_github_client = <MagicMock name='github_client' id='140300101725088'>

    @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-05-16T19:34:07.201678Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-05-16T19:34:07.202229Z [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-05-16T19:34:07.202730Z [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-05-16T19:34:07.203122Z [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-05-16T19:34:07.203340Z [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-05-16T19:34:07.201678Z'}
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-05-16T19:34:07.202229Z'}
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-05-16T19:34:07.202730Z'}
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-05-16T19:34:07.203122Z'}
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-05-16T19:34:07.203340Z'}
=========================== 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_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 0x7f8b5d9ed450>
mock_github_client = <MagicMock name='github_client' id='140236609922656'>

    @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-05-16T19:32:41.255552Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-05-16T19:32:41.256083Z [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-05-16T19:32:41.256634Z [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-05-16T19:32:41.257350Z [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-05-16T19:32:41.255552Z'}
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-05-16T19:32:41.256083Z'}
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-05-16T19:32:41.256634Z'}
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-05-16T19:32:41.257350Z'}
=========================== 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/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 0x7fe63d36fe90>
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='140626840655856'>
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 0x7fe63d36fe90>
mock_github_client = <MagicMock name='github_client' id='140626840654176'>

    @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 0x7fe63d36fe90>
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-05-16T19:30:24.907407Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-05-16T19:30:24.907950Z [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-05-16T19:30:24.908522Z [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-05-16T19:30:24.907407Z'}
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-05-16T19:30:24.907950Z'}
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-05-16T19:30:24.908522Z'}
=========================== 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 0x7f205471be10>
mock_github_client = <MagicMock name='github_client' id='139776830817008'>

    @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 0x7f205471be10>
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-05-16T19:56:19.453336Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T19:56:19.454024Z [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-05-16T19:56:19.454918Z [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-05-16T19:56:19.453336Z'}
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-05-16T19:56:19.454024Z'}
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-05-16T19:56:19.454918Z'}
=========================== 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_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 0x7ff103abd4f0>
mock_github_client = <MagicMock name='github_client' id='140673121037216'>

    @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-05-16T19:27:57.919479Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-05-16T19:27:57.920008Z [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-05-16T19:27:57.920561Z [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-05-16T19:27:57.920966Z [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-05-16T19:27:57.921160Z [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-05-16T19:27:57.919479Z'}
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-05-16T19:27:57.920008Z'}
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-05-16T19:27:57.920561Z'}
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-05-16T19:27:57.920966Z'}
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-05-16T19:27:57.921160Z'}
=========================== 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_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 0x7fa68ec11650>
mock_github_client = <MagicMock name='github_client' id='140353400481376'>

    @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-05-16T19:48:54.891428Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-05-16T19:48:54.891954Z [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-05-16T19:48:54.892498Z [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-05-16T19:48:54.893216Z [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-05-16T19:48:54.891428Z'}
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-05-16T19:48:54.891954Z'}
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-05-16T19:48:54.892498Z'}
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-05-16T19:48:54.893216Z'}
=========================== 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/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 0x7f98fa96bef0>
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='140295010424816'>
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 0x7f98fa96bef0>
mock_github_client = <MagicMock name='github_client' id='140295010423136'>

    @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 0x7f98fa96bef0>
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-05-16T19:53:07.746114Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-05-16T19:53:07.746701Z [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-05-16T19:53:07.747256Z [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-05-16T19:53:07.746114Z'}
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-05-16T19:53:07.746701Z'}
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-05-16T19:53:07.747256Z'}
=========================== 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.84s
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 0x7f1f1adebe10>
mock_github_client = <MagicMock name='github_client' id='139771567817456'>

    @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 0x7f1f1adebe10>
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-05-16T20:08:11.654686Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T20:08:11.655436Z [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-05-16T20:08:11.656337Z [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-05-16T20:08:11.654686Z'}
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-05-16T20:08:11.655436Z'}
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-05-16T20:08:11.656337Z'}
=========================== 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.20s
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 0x7fcabfbad4f0>
mock_github_client = <MagicMock name='github_client' id='140508772412320'>

    @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-05-16T19:45:34.769239Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-05-16T19:45:34.769786Z [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-05-16T19:45:34.770335Z [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-05-16T19:45:34.770767Z [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-05-16T19:45:34.770968Z [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-05-16T19:45:34.769239Z'}
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-05-16T19:45:34.769786Z'}
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-05-16T19:45:34.770335Z'}
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-05-16T19:45:34.770767Z'}
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-05-16T19:45:34.770968Z'}
=========================== 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_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 0x7fb257ded550>
mock_github_client = <MagicMock name='github_client' id='140404026254944'>

    @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-05-16T19:44:51.294267Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-05-16T19:44:51.294792Z [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-05-16T19:44:51.295359Z [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-05-16T19:44:51.296028Z [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-05-16T19:44:51.294267Z'}
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-05-16T19:44:51.294792Z'}
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-05-16T19:44:51.295359Z'}
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-05-16T19:44:51.296028Z'}
=========================== 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 0x7f5f2c89ff50>
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='140046738921456'>
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 0x7f5f2c89ff50>
mock_github_client = <MagicMock name='github_client' id='140046738919776'>

    @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 0x7f5f2c89ff50>
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-05-16T19:45:14.684695Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-05-16T19:45:14.685263Z [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-05-16T19:45:14.685790Z [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-05-16T19:45:14.684695Z'}
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-05-16T19:45:14.685263Z'}
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-05-16T19:45:14.685790Z'}
=========================== 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_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 0x7fa3d150be10>
mock_github_client = <MagicMock name='github_client' id='140341566522096'>

    @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 0x7fa3d150be10>
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-05-16T19:36:19.612764Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T19:36:19.613493Z [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-05-16T19:36:19.614381Z [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-05-16T19:36:19.612764Z'}
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-05-16T19:36:19.613493Z'}
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-05-16T19:36:19.614381Z'}
=========================== 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.15s
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 0x7f7ffc3894f0>
mock_github_client = <MagicMock name='github_client' id='140187596565408'>

    @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-05-16T19:31:57.125724Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-05-16T19:31:57.126278Z [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-05-16T19:31:57.126793Z [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-05-16T19:31:57.127221Z [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-05-16T19:31:57.127422Z [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-05-16T19:31:57.125724Z'}
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-05-16T19:31:57.126278Z'}
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-05-16T19:31:57.126793Z'}
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-05-16T19:31:57.127221Z'}
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-05-16T19:31:57.127422Z'}
=========================== 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_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 0x7f0888ae5650>
mock_github_client = <MagicMock name='github_client' id='139674695329376'>

    @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-05-16T19:34:58.311531Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-05-16T19:34:58.312095Z [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-05-16T19:34:58.312668Z [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-05-16T19:34:58.313544Z [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-05-16T19:34:58.311531Z'}
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-05-16T19:34:58.312095Z'}
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-05-16T19:34:58.312668Z'}
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-05-16T19:34:58.313544Z'}
=========================== 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/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 0x7f85de98fef0>
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='140212936185840'>
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 0x7f85de98fef0>
mock_github_client = <MagicMock name='github_client' id='140212936184160'>

    @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 0x7f85de98fef0>
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-05-16T19:39:41.977672Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-05-16T19:39:41.978241Z [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-05-16T19:39:41.978765Z [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-05-16T19:39:41.977672Z'}
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-05-16T19:39:41.978241Z'}
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-05-16T19:39:41.978765Z'}
=========================== 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_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.62s
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.64s
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.64s
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.67s
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.62s
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.63s
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.60s
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.60s
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.61s
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.64s
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.63s
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.61s
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.65s
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.61s
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.61s
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.60s
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 0x7f96fa3be9c0>

    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-05-16T19:45:36.750130Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-05-16T19:45:36.750409Z [warning  ] Missing required fields in PR event _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=warning client_ip=testclient
2026-05-16T19:45:36.751228Z [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-05-16T19:45:36.750130Z'}
WARNING  stampbot.webhook_handler:webhook_handler.py:161 {'event': 'Missing required fields in PR event', 'client_ip': 'testclient', 'level': 'warning', 'timestamp': '2026-05-16T19:45:36.750409Z'}
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_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 0x7f2fb3ea29c0>

    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-05-16T19:42:55.251435Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-05-16T19:42:55.252313Z [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-05-16T19:42:55.251435Z'}
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_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 0x7ff41d5929c0>

    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-05-16T19:53:40.554662Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-05-16T19:53:40.555559Z [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-05-16T19:53:40.554662Z'}
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.87s
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 0x7f725acae9c0>

    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-05-16T19:49:51.557514Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-05-16T19:49:51.558396Z [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-05-16T19:49:51.557514Z'}
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_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 0x7ffb89e87530>
mock_github_client = <MagicMock name='github_client' id='140718328261536'>

    @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='140718328262544'>

    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-05-16T19:48:52.636892Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T19:48:52.638263Z [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-05-16T19:48:52.636892Z'}
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-05-16T19:48:52.638263Z'}
=========================== 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 0x7f9b3a1a0830>
mock_github_client = <MagicMock name='github_client' id='140304611388656'>

    @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='140304611385968'>.call_count
E        +    where <MagicMock name='github_client.repo_has_label' id='140304611385968'> = <MagicMock name='github_client' id='140304611388656'>.repo_has_label

tests/test_webhook_handler.py:101: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-05-16T19:53:15.824602Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T19:53:15.825136Z [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-05-16T19:53:15.825665Z [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-05-16T19:53:15.824602Z'}
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-05-16T19:53:15.825136Z'}
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-05-16T19:53:15.825665Z'}
=========================== 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='140304611385968'>.call_count
 +    where <MagicMock name='github_client.repo_has_label' id='140304611385968'> = <MagicMock name='github_client' id='140304611388656'>.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 0x7f5096ff1250>
mock_github_client = <MagicMock name='github_client' id='139984169526880'>

    @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-05-16T20:09:50.493112Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-05-16T20:09:50.493672Z [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-05-16T20:09:50.493112Z'}
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-05-16T20:09:50.493672Z'}
=========================== 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.14s
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 0x7f3a5aa83590>
mock_github_client = <MagicMock name='github_client' id='139888604723056'>

    @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-05-16T19:51:42.564427Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T19:51:42.565091Z [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-05-16T19:51:42.565894Z [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-05-16T19:51:42.564427Z'}
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-05-16T19:51:42.565091Z'}
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-05-16T19:51:42.565894Z'}
=========================== 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.54s
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 0x7fdf877a69c0>

    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-05-16T19:24:52.870047Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-05-16T19:24:52.870311Z [warning  ] Missing required fields in PR event _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=warning client_ip=testclient
2026-05-16T19:24:52.871108Z [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-05-16T19:24:52.870047Z'}
WARNING  stampbot.webhook_handler:webhook_handler.py:161 {'event': 'Missing required fields in PR event', 'client_ip': 'testclient', 'level': 'warning', 'timestamp': '2026-05-16T19:24:52.870311Z'}
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.87s
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 0x7f79dfba69c0>

    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-05-16T19:38:27.890488Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-05-16T19:38:27.891396Z [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-05-16T19:38:27.890488Z'}
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.87s
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 0x7f76f4533330>
mock_github_client = <MagicMock name='github_client' id='140148882452368'>

    @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-05-16T19:47:44.126574Z [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-05-16T19:47:44.126574Z'}
=========================== 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_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 0x7f18145b69c0>

    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-05-16T19:29:01.452336Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-05-16T19:29:01.453205Z [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-05-16T19:29:01.452336Z'}
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_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 0x7ff6c4c87590>
mock_github_client = <MagicMock name='github_client' id='140697841183648'>

    @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='140697841184656'>

    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-05-16T19:56:38.711491Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T19:56:38.712850Z [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-05-16T19:56:38.711491Z'}
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-05-16T19:56:38.712850Z'}
=========================== 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_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 0x7f4fabe94830>
mock_github_client = <MagicMock name='github_client' id='139980103462128'>

    @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='139980103459440'>.call_count
E        +    where <MagicMock name='github_client.repo_has_label' id='139980103459440'> = <MagicMock name='github_client' id='139980103462128'>.repo_has_label

tests/test_webhook_handler.py:101: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-05-16T19:36:12.709649Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T19:36:12.710185Z [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-05-16T19:36:12.710743Z [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-05-16T19:36:12.709649Z'}
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-05-16T19:36:12.710185Z'}
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-05-16T19:36:12.710743Z'}
=========================== 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='139980103459440'>.call_count
 +    where <MagicMock name='github_client.repo_has_label' id='139980103459440'> = <MagicMock name='github_client' id='139980103462128'>.repo_has_label
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 165 passed, 3 deselected in 1.11s
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 0x7f06ea6f5650>
mock_github_client = <MagicMock name='github_client' id='139667744958048'>

    @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-05-16T19:31:37.892361Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-05-16T19:31:37.892889Z [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-05-16T19:31:37.892361Z'}
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-05-16T19:31:37.892889Z'}
=========================== 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.08s
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 0x7f60abfbb770>
mock_github_client = <MagicMock name='github_client' id='140053179778112'>

    @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-05-16T19:52:36.864734Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T19:52:36.865294Z [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-05-16T19:52:36.864734Z'}
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-05-16T19:52:36.865294Z'}
=========================== 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_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 0x7f900cdba9c0>

    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-05-16T19:41:46.001837Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-05-16T19:41:46.002073Z [warning  ] Missing required fields in PR event _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=warning client_ip=testclient
2026-05-16T19:41:46.002905Z [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-05-16T19:41:46.001837Z'}
WARNING  stampbot.webhook_handler:webhook_handler.py:161 {'event': 'Missing required fields in PR event', 'client_ip': 'testclient', 'level': 'warning', 'timestamp': '2026-05-16T19:41:46.002073Z'}
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_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 0x7fc1226c69c0>

    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-05-16T19:30:22.264327Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-05-16T19:30:22.265225Z [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-05-16T19:30:22.264327Z'}
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_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.61s
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 0x7f65524b3770>
mock_github_client = <MagicMock name='github_client' id='140073149937520'>

    @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='140073149938528'>

    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-05-16T20:04:02.901614Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'labeled'}
2026-05-16T20:04:02.902930Z [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-05-16T20:04:02.901614Z'}
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-05-16T20:04:02.902930Z'}
=========================== 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.62s
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.60s
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 0x7ff9812e3250>

    @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-05-16T19:59:30.559728Z [info     ] Received unknown event         _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'unknown', 'action': ''}
2026-05-16T19:59:30.559909Z [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-05-16T19:59:30.559728Z'}
WARNING  stampbot.webhook_handler:webhook_handler.py:161 {'event': 'Missing required fields in PR event', 'level': 'warning', 'timestamp': '2026-05-16T19:59:30.559909Z'}
=========================== 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_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 0x7fde722871d0>
mock_github_client = <MagicMock name='github_client' id='140593374794416'>

    @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-05-16T20:08:14.704298Z [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-05-16T20:08:14.704298Z'}
=========================== 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.65s
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 0x7fdc495a29c0>

    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-05-16T20:01:15.405908Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-05-16T20:01:15.406807Z [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-05-16T20:01:15.405908Z'}
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_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 0x7fb1a4d929c0>

    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-05-16T19:37:02.096149Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-05-16T19:37:02.097051Z [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-05-16T19:37:02.096149Z'}
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 0x7fbaf0497b30>
mock_github_client = <MagicMock name='github_client' id='140440873397152'>

    @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='140440873398160'>

    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-05-16T20:07:40.409093Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T20:07:40.410540Z [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-05-16T20:07:40.409093Z'}
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-05-16T20:07:40.410540Z'}
=========================== 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.66s
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 0x7f0ff70a0830>
mock_github_client = <MagicMock name='github_client' id='139706485845232'>

    @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='139706485842544'>.call_count
E        +    where <MagicMock name='github_client.repo_has_label' id='139706485842544'> = <MagicMock name='github_client' id='139706485845232'>.repo_has_label

tests/test_webhook_handler.py:101: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-05-16T19:34:25.124012Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T19:34:25.124573Z [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-05-16T19:34:25.125083Z [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-05-16T19:34:25.124012Z'}
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-05-16T19:34:25.124573Z'}
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-05-16T19:34:25.125083Z'}
=========================== 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='139706485842544'>.call_count
 +    where <MagicMock name='github_client.repo_has_label' id='139706485842544'> = <MagicMock name='github_client' id='139706485845232'>.repo_has_label
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 165 passed, 3 deselected in 1.08s
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 0x7f97781dd450>
mock_github_client = <MagicMock name='github_client' id='140288594077280'>

    @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-05-16T19:35:54.593056Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-05-16T19:35:54.593613Z [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-05-16T19:35:54.593056Z'}
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-05-16T19:35:54.593613Z'}
=========================== 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_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 0x7f8a5d887530>
mock_github_client = <MagicMock name='github_client' id='140232250455920'>

    @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-05-16T19:57:36.875525Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T19:57:36.876207Z [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-05-16T19:57:36.876998Z [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-05-16T19:57:36.875525Z'}
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-05-16T19:57:36.876207Z'}
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-05-16T19:57:36.876998Z'}
=========================== 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/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 0x7f9cb1b07250>

    @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-05-16T19:39:06.521460Z [info     ] Received unknown event         _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'unknown', 'action': ''}
2026-05-16T19:39:06.521646Z [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-05-16T19:39:06.521460Z'}
WARNING  stampbot.webhook_handler:webhook_handler.py:161 {'event': 'Missing required fields in PR event', 'level': 'warning', 'timestamp': '2026-05-16T19:39:06.521646Z'}
=========================== 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_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.71s
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 0x7f8205eda9c0>

    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-05-16T20:02:01.476380Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-05-16T20:02:01.477299Z [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-05-16T20:02:01.476380Z'}
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.87s
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 0x7f31faa13250>

    @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-05-16T20:07:21.335661Z [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-05-16T20:07:21.335661Z'}
=========================== 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.11s
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.60s
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.61s
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 0x7fbdbceabad0>
mock_github_client = <MagicMock name='github_client' id='140452893767536'>

    @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-05-16T19:59:10.694594Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T19:59:10.695310Z [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-05-16T19:59:10.696102Z [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-05-16T19:59:10.694594Z'}
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-05-16T19:59:10.695310Z'}
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-05-16T19:59:10.696102Z'}
=========================== 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_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.61s
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.70s
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 0x7fb8765ca9c0>

    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-05-16T19:48:00.870329Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-05-16T19:48:00.871399Z [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-05-16T19:48:00.870329Z'}
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 0x7f44365a0d10>
mock_github_client = <MagicMock name='github_client' id='139930947251776'>

    @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='139930947252784'>

    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-05-16T19:46:42.731186Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T19:46:42.732593Z [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-05-16T19:46:42.731186Z'}
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-05-16T19:46:42.732593Z'}
=============================== 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.66s
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 0x7f27c8120830>
mock_github_client = <MagicMock name='github_client' id='139808777514224'>

    @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='139808777511536'>.call_count
E        +    where <MagicMock name='github_client.repo_has_label' id='139808777511536'> = <MagicMock name='github_client' id='139808777514224'>.repo_has_label

tests/test_webhook_handler.py:101: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-05-16T19:52:47.482249Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T19:52:47.482792Z [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-05-16T19:52:47.483334Z [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-05-16T19:52:47.482249Z'}
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-05-16T19:52:47.482792Z'}
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-05-16T19:52:47.483334Z'}
=============================== 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='139808777511536'>.call_count
 +    where <MagicMock name='github_client.repo_has_label' id='139808777511536'> = <MagicMock name='github_client' id='139808777514224'>.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 0x7f9b5e9d1250>
mock_github_client = <MagicMock name='github_client' id='140305346127456'>

    @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-05-16T19:23:14.162031Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-05-16T19:23:14.162600Z [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-05-16T19:23:14.162031Z'}
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-05-16T19:23:14.162600Z'}
=============================== 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.07s
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.60s
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 0x7f69f46ba9c0>

    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-05-16T19:48:32.343857Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-05-16T19:48:32.344096Z [warning  ] Missing required fields in PR event _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=warning client_ip=testclient
2026-05-16T19:48:32.344910Z [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-05-16T19:48:32.343857Z'}
WARNING  stampbot.webhook_handler:webhook_handler.py:161 {'event': 'Missing required fields in PR event', 'client_ip': 'testclient', 'level': 'warning', 'timestamp': '2026-05-16T19:48:32.344096Z'}
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.85s
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 0x7fd0703ca9c0>

    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-05-16T19:24:30.655990Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-05-16T19:24:30.656893Z [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-05-16T19:24:30.655990Z'}
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.85s
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 0x7f1a21abe9c0>

    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-05-16T19:36:46.187854Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-05-16T19:36:46.188755Z [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-05-16T19:36:46.187854Z'}
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.85s
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 0x7f3b931fb250>

    @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-05-16T19:54:47.969168Z [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-05-16T19:54:47.969168Z'}
=============================== 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.07s
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 0x7f7df87c5310>
mock_github_client = <MagicMock name='github_client' id='140179017382416'>

    @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='140179017383424'>

    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-05-16T19:32:19.704045Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'labeled'}
2026-05-16T19:32:19.705419Z [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-05-16T19:32:19.704045Z'}
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-05-16T19:32:19.705419Z'}
=============================== 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.63s
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.62s
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.61s
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 0x7fc801de9010>
mock_github_client = <MagicMock name='github_client' id='140497000184336'>

    @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-05-16T19:25:58.063663Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T19:25:58.064356Z [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-05-16T19:25:58.065141Z [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-05-16T19:25:58.063663Z'}
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-05-16T19:25:58.064356Z'}
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-05-16T19:25:58.065141Z'}
=============================== 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.55s
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 0x7fce1bcaf770>
mock_github_client = <MagicMock name='github_client' id='140523207181376'>

    @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-05-16T19:58:57.164929Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T19:58:57.165493Z [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-05-16T19:58:57.164929Z'}
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-05-16T19:58:57.165493Z'}
=========================== 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.59s
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 0x7f5bf769b7d0>
mock_github_client = <MagicMock name='github_client' id='140032970310720'>

    @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-05-16T19:28:19.829163Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T19:28:19.829723Z [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-05-16T19:28:19.829163Z'}
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-05-16T19:28:19.829723Z'}
=========================== 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 0x7f0eb25bb7d0>
mock_github_client = <MagicMock name='github_client' id='139701099463744'>

    @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-05-16T20:03:06.253242Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T20:03:06.253791Z [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-05-16T20:03:06.253242Z'}
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-05-16T20:03:06.253791Z'}
=========================== 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.59s
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 0x7fa52acb3770>
mock_github_client = <MagicMock name='github_client' id='140347365147712'>

    @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-05-16T19:34:41.688503Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T19:34:41.689036Z [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-05-16T19:34:41.688503Z'}
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-05-16T19:34:41.689036Z'}
=========================== 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.54s
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.63s
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 0x7f3cf4893020>
mock_github_client = <MagicMock name='github_client' id='139899620466976'>

    @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-05-16T19:43:34.609893Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-05-16T19:43:34.610517Z [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-05-16T19:43:34.609893Z'}
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-05-16T19:43:34.610517Z'}
=========================== 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.18s
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 0x7f921a6671d0>
mock_github_client = <MagicMock name='github_client' id='140265485365136'>

    @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-05-16T20:07:18.939765Z [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-05-16T20:07:18.939765Z'}
=========================== 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.16s
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 0x7f29b245f330>
mock_github_client = <MagicMock name='github_client' id='139817061805968'>

    @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-05-16T19:41:54.588566Z [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-05-16T19:41:54.588566Z'}
=========================== 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_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 0x7f586e875ff0>
mock_github_client = <MagicMock name='github_client' id='140017788710800'>

    @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-05-16T19:36:01.748019Z [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-05-16T19:36:01.748019Z'}
=========================== 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_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.60s
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 0x7fdfa6fe7020>
mock_github_client = <MagicMock name='github_client' id='140598464897312'>

    @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-05-16T19:59:28.409790Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-05-16T19:59:28.410377Z [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-05-16T19:59:28.409790Z'}
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-05-16T19:59:28.410377Z'}
=========================== 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.12s
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 0x7fd106e4f1d0>
mock_github_client = <MagicMock name='github_client' id='140535741050768'>

    @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-05-16T20:05:05.904002Z [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-05-16T20:05:05.904002Z'}
=========================== 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.14s
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.59s
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.61s
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.62s
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.68s
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.71s
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 0x7f04906a7770>

    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 0x7f04904b9a80>, 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.61s
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 0x7f4a2055f1d0>
mock_github_client = <MagicMock name='github_client' id='139956347301776'>

    @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 0x7f4a2055f1d0>
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-05-16T20:04:56.524337Z [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-05-16T20:04:56.524337Z'}
=========================== 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.20s
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 0x7fce6435f330>
mock_github_client = <MagicMock name='github_client' id='140524421754768'>

    @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 0x7fce6435f330>
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-05-16T19:58:13.569033Z [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-05-16T19:58:13.569033Z'}
=========================== 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/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 0x7fcc5e757330>
mock_github_client = <MagicMock name='github_client' id='140515735334800'>

    @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 0x7fcc5e757330>
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-05-16T19:53:20.185268Z [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-05-16T19:53:20.185268Z'}
=========================== 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.18s
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 0x7f8dbbf4b330>
mock_github_client = <MagicMock name='github_client' id='140246720982928'>

    @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 0x7f8dbbf4b330>
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-05-16T19:56:29.481791Z [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-05-16T19:56:29.481791Z'}
=========================== 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_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 0x7f73d429f770>

    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 0x7f73d42a2e40>

    @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.18s
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 0x7f53bfeb45a0>

    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 0x7f53c41aee40>
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.19s
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 0x7fc0c9fe3e10>
mock_github_client = <MagicMock name='github_client' id='140465995629296'>

    @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-05-16T19:47:58.829388Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T19:47:58.829580Z [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-05-16T19:47:58.829388Z'}
WARNING  stampbot.webhook_handler:webhook_handler.py:161 {'event': 'Missing required fields in PR event', 'level': 'warning', 'timestamp': '2026-05-16T19:47:58.829580Z'}
=========================== 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.10s
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 0x7f6178a13e10>
mock_github_client = <MagicMock name='github_client' id='140056610767600'>

    @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-05-16T19:40:07.753429Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T19:40:07.754062Z [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-05-16T19:40:07.754921Z [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-05-16T19:40:07.753429Z'}
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-05-16T19:40:07.754062Z'}
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-05-16T19:40:07.754921Z'}
=========================== 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 0x7f274954b330>
mock_github_client = <MagicMock name='github_client' id='139806711230352'>

    @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-05-16T20:05:34.002723Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-05-16T20:05:34.002916Z [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-05-16T20:05:34.002723Z'}
WARNING  stampbot.webhook_handler:webhook_handler.py:410 {'event': 'Missing required fields in comment event', 'level': 'warning', 'timestamp': '2026-05-16T20:05:34.002916Z'}
=========================== 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.15s
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 0x7f1d61b5f1d0>
mock_github_client = <MagicMock name='github_client' id='139764170583952'>

    @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-05-16T19:58:38.143668Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-05-16T19:58:38.144244Z [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-05-16T19:58:38.143668Z'}
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-05-16T19:58:38.144244Z'}
=========================== 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/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 0x7fa267357330>
mock_github_client = <MagicMock name='github_client' id='140335493476240'>

    @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-05-16T19:33:53.418851Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-05-16T19:33:53.419401Z [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-05-16T19:33:53.418851Z'}
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-05-16T19:33:53.419401Z'}
=========================== 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: 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 0x7f53de045ff0>
mock_github_client = <MagicMock name='github_client' id='139998184337296'>

    @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-05-16T19:36:21.976184Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-05-16T19:36:21.976751Z [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-05-16T19:36:21.976184Z'}
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-05-16T19:36:21.976751Z'}
=========================== 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/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.61s
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 0x7fb3fcff1450>
mock_github_client = <MagicMock name='github_client' id='140411082466912'>

    @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='140411082465904'>.call_count
E        +    where <MagicMock name='github_client.dismiss_approval' id='140411082465904'> = <MagicMock name='github_client' id='140411082466912'>.dismiss_approval

tests/test_webhook_handler.py:155: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-05-16T19:52:05.864076Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-05-16T19:52:05.864628Z [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-05-16T19:52:05.865142Z [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-05-16T19:52:05.865567Z [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-05-16T19:52:05.864076Z'}
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-05-16T19:52:05.864628Z'}
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-05-16T19:52:05.865142Z'}
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-05-16T19:52:05.865567Z'}
=========================== 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='140411082465904'>.call_count
 +    where <MagicMock name='github_client.dismiss_approval' id='140411082465904'> = <MagicMock name='github_client' id='140411082466912'>.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 0x7ff8fdeb3770>

    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 0x7ff8fdcc5990>, 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/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 0x7f3dfec9f770>

    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 0x7f3dfeca2e40>

    @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.19s
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 0x7f64ddda05a0>

    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 0x7f64ddf96e40>
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.22s
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 0x7fd6565ba9c0>

    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-05-16T19:46:46.897036Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-05-16T19:46:46.897287Z [warning  ] Missing required fields in PR event _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=warning client_ip=testclient
2026-05-16T19:46:46.898096Z [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-05-16T19:46:46.897036Z'}
WARNING  stampbot.webhook_handler:webhook_handler.py:161 {'event': 'Missing required fields in PR event', 'client_ip': 'testclient', 'level': 'warning', 'timestamp': '2026-05-16T19:46:46.897287Z'}
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/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 0x7f578ab5dff0>
mock_github_client = <MagicMock name='github_client' id='140013966569360'>

    @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-05-16T19:49:44.988946Z [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-05-16T19:49:44.988946Z'}
=========================== 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/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 0x7f29d6eefe10>
mock_github_client = <MagicMock name='github_client' id='139817672688368'>

    @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-05-16T19:59:24.389781Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T19:59:24.389962Z [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-05-16T19:59:24.389781Z'}
WARNING  stampbot.webhook_handler:webhook_handler.py:161 {'event': 'Missing required fields in PR event', 'level': 'warning', 'timestamp': '2026-05-16T19:59:24.389962Z'}
=========================== 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 0x7fcde63f3e10>
mock_github_client = <MagicMock name='github_client' id='140522304242416'>

    @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-05-16T20:06:02.796259Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T20:06:02.796968Z [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-05-16T20:06:02.797548Z [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-05-16T20:06:02.796259Z'}
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-05-16T20:06:02.796968Z'}
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-05-16T20:06:02.797548Z'}
=========================== 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.12s
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 0x7fdc0f67f590>
mock_github_client = <MagicMock name='github_client' id='140583129099168'>

    @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='140583129100176'>

    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-05-16T19:56:55.654556Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T19:56:55.655889Z [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-05-16T19:56:55.654556Z'}
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-05-16T19:56:55.655889Z'}
=========================== 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.62s
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 0x7f89dfe9c830>
mock_github_client = <MagicMock name='github_client' id='140230083833072'>

    @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='140230083830384'>.call_count
E        +    where <MagicMock name='github_client.repo_has_label' id='140230083830384'> = <MagicMock name='github_client' id='140230083833072'>.repo_has_label

tests/test_webhook_handler.py:101: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-05-16T20:05:23.103212Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T20:05:23.103801Z [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-05-16T20:05:23.104381Z [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-05-16T20:05:23.103212Z'}
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-05-16T20:05:23.103801Z'}
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-05-16T20:05:23.104381Z'}
=========================== 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='140230083830384'>.call_count
 +    where <MagicMock name='github_client.repo_has_label' id='140230083830384'> = <MagicMock name='github_client' id='140230083833072'>.repo_has_label
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 165 passed, 3 deselected in 1.11s
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.62s
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 0x7efee6813e10>
mock_github_client = <MagicMock name='github_client' id='139633252401904'>

    @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-05-16T19:40:54.724583Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T19:40:54.725256Z [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-05-16T19:40:54.724583Z'}
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-05-16T19:40:54.725256Z'}
=========================== 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.08s
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 0x7fe62bbdfe10>
mock_github_client = <MagicMock name='github_client' id='140626549392112'>

    @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-05-16T19:38:15.075366Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T19:38:15.076034Z [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-05-16T19:38:15.075366Z'}
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-05-16T19:38:15.076034Z'}
=========================== 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 0x7ff99a6831d0>
mock_github_client = <MagicMock name='github_client' id='140710008897952'>

    @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-05-16T19:42:32.944515Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T19:42:32.945779Z [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-05-16T19:42:32.944515Z'}
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-05-16T19:42:32.945779Z'}
=========================== 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.64s
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 0x7f0cdd513e10>
mock_github_client = <MagicMock name='github_client' id='139693227803376'>

    @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-05-16T19:35:49.730045Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T19:35:49.730702Z [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-05-16T19:35:49.731581Z [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-05-16T19:35:49.730045Z'}
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-05-16T19:35:49.730702Z'}
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-05-16T19:35:49.731581Z'}
=========================== 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: 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.64s
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 0x7f5e8151fe10>
mock_github_client = <MagicMock name='github_client' id='140043871683312'>

    @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-05-16T19:58:04.078368Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T19:58:04.079017Z [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-05-16T19:58:04.079887Z [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-05-16T19:58:04.078368Z'}
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-05-16T19:58:04.079017Z'}
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-05-16T19:58:04.079887Z'}
=========================== 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: 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 0x7ff3da53be10>
mock_github_client = <MagicMock name='github_client' id='140685315080944'>

    @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 0x7ff3da16dd70>()
E        +    where <built-in method lower of str object at 0x7ff3da16dd70> = 'Failed to approve PR'.lower

tests/test_webhook_handler.py:71: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-05-16T19:25:35.143102Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T19:25:35.143796Z [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-05-16T19:25:35.144698Z [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-05-16T19:25:35.143102Z'}
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-05-16T19:25:35.143796Z'}
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-05-16T19:25:35.144698Z'}
=========================== 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 0x7ff3da16dd70>()
 +    where <built-in method lower of str object at 0x7ff3da16dd70> = 'Failed to approve PR'.lower
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 163 passed, 3 deselected in 1.08s
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 0x7f247f3f1550>
mock_github_client = <MagicMock name='github_client' id='139794792457824'>

    @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-05-16T19:47:19.657499Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-05-16T19:47:19.658084Z [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-05-16T19:47:19.657499Z'}
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-05-16T19:47:19.658084Z'}
=========================== 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.13s
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 0x7f3b74be1550>
mock_github_client = <MagicMock name='github_client' id='139893400479328'>

    @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-05-16T19:55:31.671289Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-05-16T19:55:31.671828Z [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-05-16T19:55:31.671289Z'}
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-05-16T19:55:31.671828Z'}
=========================== 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: 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.66s
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 0x7f9890cf1450>
mock_github_client = <MagicMock name='github_client' id='140293306164832'>

    @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-05-16T19:42:35.139503Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-05-16T19:42:35.140035Z [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-05-16T19:42:35.140570Z [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-05-16T19:42:35.139503Z'}
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-05-16T19:42:35.140035Z'}
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-05-16T19:42:35.140570Z'}
=========================== 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/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 0x7f581dbc9450>
mock_github_client = <MagicMock name='github_client' id='140016501974624'>

    @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 0x7f581d9a5160>()
E        +    where <built-in method lower of str object at 0x7f581d9a5160> = 'Failed to dismiss approvals'.lower

tests/test_webhook_handler.py:153: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-05-16T19:23:24.586170Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-05-16T19:23:24.586723Z [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-05-16T19:23:24.587248Z [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-05-16T19:23:24.586170Z'}
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-05-16T19:23:24.586723Z'}
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-05-16T19:23:24.587248Z'}
=========================== 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 0x7f581d9a5160>()
 +    where <built-in method lower of str object at 0x7f581d9a5160> = 'Failed to dismiss approvals'.lower
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 169 passed, 3 deselected in 1.09s
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 0x7f8bfbf5f1d0>
mock_github_client = <MagicMock name='github_client' id='140239204904848'>

    @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-05-16T19:30:47.220730Z [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-05-16T19:30:47.220730Z'}
=========================== 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/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 0x7f28ec75b1d0>
mock_github_client = <MagicMock name='github_client' id='139813743046544'>

    @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-05-16T19:49:26.932963Z [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-05-16T19:49:26.932963Z'}
=========================== 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/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 0x7fbfb105dff0>
mock_github_client = <MagicMock name='github_client' id='140461285928848'>

    @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-05-16T20:04:05.210541Z [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-05-16T20:04:05.210541Z'}
=========================== 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: 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 0x7fd8a0957330>
mock_github_client = <MagicMock name='github_client' id='140568384302992'>

    @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-05-16T19:36:41.553884Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-05-16T19:36:41.554072Z [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-05-16T19:36:41.553884Z'}
WARNING  stampbot.webhook_handler:webhook_handler.py:410 {'event': 'Missing required fields in comment event', 'level': 'warning', 'timestamp': '2026-05-16T19:36:41.554072Z'}
=========================== 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: 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 0x7fa57615b1d0>
mock_github_client = <MagicMock name='github_client' id='140348627955600'>

    @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-05-16T19:31:17.065485Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-05-16T19:31:17.066015Z [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-05-16T19:31:17.065485Z'}
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-05-16T19:31:17.066015Z'}
=========================== 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.11s
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 0x7f39ff151ff0>
mock_github_client = <MagicMock name='github_client' id='139887069933456'>

    @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-05-16T19:46:37.239825Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-05-16T19:46:37.240379Z [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-05-16T19:46:37.239825Z'}
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-05-16T19:46:37.240379Z'}
=========================== 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.14s
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 0x7f71646571d0>
mock_github_client = <MagicMock name='github_client' id='140124992892816'>

    @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-05-16T19:48:49.972224Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-05-16T19:48:49.972760Z [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-05-16T19:48:49.972224Z'}
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-05-16T19:48:49.972760Z'}
=========================== 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: 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 0x7f72ea5571d0>
mock_github_client = <MagicMock name='github_client' id='140131534958480'>

    @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='140131534960832'>

    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-05-16T19:48:21.720854Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-05-16T19:48:21.721447Z [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-05-16T19:48:21.720854Z'}
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-05-16T19:48:21.721447Z'}
=========================== 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 0x7fb553459ff0>
mock_github_client = <MagicMock name='github_client' id='140416763375504'>

    @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-05-16T19:31:08.105706Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-05-16T19:31:08.106254Z [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-05-16T19:31:08.105706Z'}
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-05-16T19:31:08.106254Z'}
=========================== 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 0x7f54a2c5dff0>
mock_github_client = <MagicMock name='github_client' id='140001485352848'>

    @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-05-16T19:35:02.424680Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-05-16T19:35:02.425244Z [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-05-16T19:35:02.424680Z'}
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-05-16T19:35:02.425244Z'}
=========================== 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: 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.63s
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.67s
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 0x7fef6025dff0>
mock_github_client = <MagicMock name='github_client' id='140666087501712'>

    @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-05-16T19:24:19.225265Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-05-16T19:24:19.225804Z [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-05-16T19:24:19.225265Z'}
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-05-16T19:24:19.225804Z'}
=========================== 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 0x7f4f7435dff0>
mock_github_client = <MagicMock name='github_client' id='139979229327248'>

    @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 0x7f4f74645870>()
E        +    where <built-in method lower of str object at 0x7f4f74645870> = 'Failed to approve PR'.lower

tests/test_webhook_handler.py:260: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-05-16T19:51:17.441516Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-05-16T19:51:17.442050Z [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-05-16T19:51:17.441516Z'}
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-05-16T19:51:17.442050Z'}
=========================== 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 0x7f4f74645870>()
 +    where <built-in method lower of str object at 0x7f4f74645870> = '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.61s
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.60s
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 0x7fd0447377f0>
mock_github_client = <MagicMock name='github_client' id='140532479133744'>

    @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-05-16T20:09:28.778621Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-05-16T20:09:28.779182Z [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-05-16T20:09:28.778621Z'}
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-05-16T20:09:28.779182Z'}
=========================== 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.20s
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 0x7fa852d1f7f0>
mock_github_client = <MagicMock name='github_client' id='140360921548848'>

    @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 0x7fa853bb9200>()
E        +    where <built-in method lower of str object at 0x7fa853bb9200> = 'Failed to dismiss approvals'.lower

tests/test_webhook_handler.py:290: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-05-16T19:26:26.698835Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-05-16T19:26:26.699398Z [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-05-16T19:26:26.698835Z'}
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-05-16T19:26:26.699398Z'}
=========================== 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 0x7fa853bb9200>()
 +    where <built-in method lower of str object at 0x7fa853bb9200> = 'Failed to dismiss approvals'.lower
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 179 passed, 3 deselected in 1.13s
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 0x7f4459b4e7a0>
mock_github_client = <MagicMock name='github_client' id='139931539660416'>

    @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-05-16T19:34:09.321099Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T19:34:09.321662Z [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-05-16T19:34:09.322491Z [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-05-16T19:34:09.321099Z'}
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-05-16T19:34:09.321662Z'}
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-05-16T19:34:09.322491Z'}
=========================== 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 0x7fc9127af3b0>
mock_github_client = <MagicMock name='github_client' id='140501572830608'>

    @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-05-16T20:00:20.782864Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T20:00:20.783555Z [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-05-16T20:00:20.784385Z [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-05-16T20:00:20.782864Z'}
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-05-16T20:00:20.783555Z'}
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-05-16T20:00:20.784385Z'}
=========================== 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.59s
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 0x7f7e22da3830>
mock_github_client = <MagicMock name='github_client' id='140179728483392'>

    @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-05-16T19:37:34.419550Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T19:37:34.420231Z [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-05-16T19:37:34.419550Z'}
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-05-16T19:37:34.420231Z'}
=========================== 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.60s
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 0x7f00601fbe10>
mock_github_client = <MagicMock name='github_client' id='139639585719024'>

    @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='139639585719360'>
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-05-16T19:42:48.291818Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T19:42:48.292523Z [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-05-16T19:42:48.293402Z [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-05-16T19:42:48.293733Z [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-05-16T19:42:48.291818Z'}
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-05-16T19:42:48.292523Z'}
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-05-16T19:42:48.293402Z'}
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-05-16T19:42:48.293733Z'}
=========================== 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.13s
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.60s
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.66s
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 0x7fd0177ed450>
mock_github_client = <MagicMock name='github_client' id='140531786245728'>

    @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='140531786244720'>.call_count
E        +    where <MagicMock name='github_client.dismiss_approval' id='140531786244720'> = <MagicMock name='github_client' id='140531786245728'>.dismiss_approval

tests/test_webhook_handler.py:155: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-05-16T19:37:13.640250Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-05-16T19:37:13.640773Z [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-05-16T19:37:13.641318Z [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-05-16T19:37:13.641720Z [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-05-16T19:37:13.640250Z'}
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-05-16T19:37:13.640773Z'}
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-05-16T19:37:13.641318Z'}
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-05-16T19:37:13.641720Z'}
=========================== 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='140531786244720'>.call_count
 +    where <MagicMock name='github_client.dismiss_approval' id='140531786244720'> = <MagicMock name='github_client' id='140531786245728'>.dismiss_approval
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 169 passed, 3 deselected in 1.09s
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.60s
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 0x7fc7503e1250>
mock_github_client = <MagicMock name='github_client' id='140494083615984'>

    @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-05-16T19:28:10.377818Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'labeled'}
2026-05-16T19:28:10.378375Z [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-05-16T19:28:10.378896Z [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-05-16T19:28:10.379243Z [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-05-16T19:28:10.377818Z'}
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-05-16T19:28:10.378375Z'}
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-05-16T19:28:10.378896Z'}
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-05-16T19:28:10.379243Z'}
=========================== 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.09s
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 0x7fa8ac5b54f0>
mock_github_client = <MagicMock name='github_client' id='140362418498464'>

    @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-05-16T19:58:41.825344Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-05-16T19:58:41.825881Z [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-05-16T19:58:41.826410Z [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-05-16T19:58:41.826841Z [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-05-16T19:58:41.825344Z'}
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-05-16T19:58:41.825881Z'}
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-05-16T19:58:41.826410Z'}
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-05-16T19:58:41.826841Z'}
=========================== 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/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 0x7f029aa01450>
mock_github_client = <MagicMock name='github_client' id='139649224959584'>

    @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-05-16T19:41:07.975056Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-05-16T19:41:07.975622Z [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-05-16T19:41:07.976128Z [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-05-16T19:41:07.975056Z'}
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-05-16T19:41:07.975622Z'}
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-05-16T19:41:07.976128Z'}
=========================== 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/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 0x7f93e23c45a0>

    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-05-16T19:25:50.813674Z [info     ] Received ping event            _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info client_ip=testclient extra={'event_type': 'ping', 'action': ''}
2026-05-16T19:25:50.814621Z [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-05-16T19:25:50.813674Z'}
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.84s
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.63s
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 0x7f3131e901d0>
mock_github_client = <MagicMock name='github_client' id='139849261797728'>

    @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-05-16T20:06:22.283156Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-05-16T20:06:22.283745Z [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-05-16T20:06:22.284302Z [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-05-16T20:06:22.284676Z [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-05-16T20:06:22.283156Z'}
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-05-16T20:06:22.283745Z'}
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-05-16T20:06:22.284302Z'}
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-05-16T20:06:22.284676Z'}
=========================== 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 0x7f448ae06580>
mock_github_client = <MagicMock name='github_client' id='139932364595152'>

    @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-05-16T19:24:57.818219Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T19:24:57.819486Z [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-05-16T19:24:57.818219Z'}
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-05-16T19:24:57.819486Z'}
=========================== 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 0x7f820ff1b170>
mock_github_client = <MagicMock name='github_client' id='140196590295360'>

    @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='140196590298048'>

    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-05-16T19:51:15.192520Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T19:51:15.193952Z [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-05-16T19:51:15.192520Z'}
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-05-16T19:51:15.193952Z'}
=========================== 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.68s
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 0x7efd5c2a54f0>
mock_github_client = <MagicMock name='github_client' id='139626565514112'>

    @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-05-16T20:05:48.057152Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-05-16T20:05:48.058161Z [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-05-16T20:05:48.058672Z [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-05-16T20:05:48.057152Z'}
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-05-16T20:05:48.058161Z'}
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-05-16T20:05:48.058672Z'}
=========================== 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.15s
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 0x7ff7e9887530>
mock_github_client = <MagicMock name='github_client' id='140702750524816'>

    @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-05-16T19:27:42.904819Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T19:27:42.905511Z [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-05-16T19:27:42.906319Z [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-05-16T19:27:42.904819Z'}
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-05-16T19:27:42.905511Z'}
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-05-16T19:27:42.906319Z'}
=========================== 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.54s
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 0x7fe24a0735f0>
mock_github_client = <MagicMock name='github_client' id='140609880110960'>

    @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-05-16T19:24:28.702531Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T19:24:28.703226Z [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-05-16T19:24:28.704007Z [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-05-16T19:24:28.702531Z'}
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-05-16T19:24:28.703226Z'}
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-05-16T19:24:28.704007Z'}
=========================== 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 0x7f3fb1a9fad0>
mock_github_client = <MagicMock name='github_client' id='139911539505008'>

    @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-05-16T19:46:05.014920Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T19:46:05.015476Z [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-05-16T19:46:05.016276Z [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-05-16T19:46:05.014920Z'}
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-05-16T19:46:05.015476Z'}
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-05-16T19:46:05.016276Z'}
=========================== 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.58s
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.69s
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.61s
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.60s
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.60s
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 0x7fe49d1ae030>
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='140619858200288'>
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 0x7fe49d1ae030>
mock_github_client = <MagicMock name='github_client' id='140619858199952'>

    @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 0x7fe49d1ae030>
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-05-16T19:45:20.299756Z [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-05-16T19:45:20.299756Z'}
=========================== 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 0x7f736c3b3ad0>
mock_github_client = <MagicMock name='github_client' id='140133714701216'>

    @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-05-16T20:00:18.034637Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T20:00:18.035653Z [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-05-16T20:00:18.036479Z [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-05-16T20:00:18.034637Z'}
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-05-16T20:00:18.035653Z'}
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-05-16T20:00:18.036479Z'}
=========================== 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.59s
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 0x7f3297c301d0>
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='139855271090160'>
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 0x7f3297c301d0>
mock_github_client = <MagicMock name='github_client' id='139855271088480'>

    @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 0x7f3297c301d0>
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-05-16T19:42:14.144608Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-05-16T19:42:14.145156Z [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-05-16T19:42:14.145701Z [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-05-16T19:42:14.144608Z'}
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-05-16T19:42:14.145156Z'}
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-05-16T19:42:14.145701Z'}
=========================== 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.82s
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.61s
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.65s
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.70s
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.60s
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 0x7f4c6da5f330>
mock_github_client = <MagicMock name='github_client' id='139966234357648'>

    @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 0x7f4c6da5f330>
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-05-16T19:38:54.945681Z [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-05-16T19:38:54.945681Z'}
=========================== 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 0x7fd501257330>
mock_github_client = <MagicMock name='github_client' id='140552824483728'>

    @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 0x7fd501257330>
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-05-16T19:31:14.842408Z [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-05-16T19:31:14.842408Z'}
=========================== 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/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 0x7ff76713dff0>
mock_github_client = <MagicMock name='github_client' id='140700563500944'>

    @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 0x7ff76713dff0>
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-05-16T20:04:23.163698Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-05-16T20:04:23.164271Z [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-05-16T20:04:23.163698Z'}
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-05-16T20:04:23.164271Z'}
=========================== 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 0x7fb872f5f330>
mock_github_client = <MagicMock name='github_client' id='140430179921808'>

    @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-05-16T19:51:08.007554Z [info     ] Received issue_comment event   _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'issue_comment', 'action': 'created'}
2026-05-16T19:51:08.008090Z [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-05-16T19:51:08.007554Z'}
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-05-16T19:51:08.008090Z'}
=========================== 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/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 0x7f02dc1af770>

    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 0x7f02d7ebd940>
key = <bound method WebhookHandler.webhook_secret of <stampbot.webhook_handler.WebhookHandler object at 0x7f02dc1b2e40>>
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 0x7f381dfcc830>
mock_github_client = <MagicMock name='github_client' id='139878935538928'>

    @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='139878935536240'>.call_count
E        +    where <MagicMock name='github_client.repo_has_label' id='139878935536240'> = <MagicMock name='github_client' id='139878935538928'>.repo_has_label

tests/test_webhook_handler.py:101: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-05-16T19:28:52.002393Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T19:28:52.002930Z [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-05-16T19:28:52.003466Z [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-05-16T19:28:52.002393Z'}
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-05-16T19:28:52.002930Z'}
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-05-16T19:28:52.003466Z'}
=========================== 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='139878935536240'>.call_count
 +    where <MagicMock name='github_client.repo_has_label' id='139878935536240'> = <MagicMock name='github_client' id='139878935538928'>.repo_has_label
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 165 passed, 3 deselected in 1.07s
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 0x7ff76440be10>
mock_github_client = <MagicMock name='github_client' id='140700514009840'>

    @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-05-16T19:37:11.471483Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'opened'}
2026-05-16T19:37:11.472113Z [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-05-16T19:37:11.471483Z'}
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-05-16T19:37:11.472113Z'}
=========================== 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 0x7f8313101450>
mock_github_client = <MagicMock name='github_client' id='140201004639840'>

    @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='140201004638832'>.call_count
E        +    where <MagicMock name='github_client.dismiss_approval' id='140201004638832'> = <MagicMock name='github_client' id='140201004639840'>.dismiss_approval

tests/test_webhook_handler.py:155: AssertionError
----------------------------- Captured stdout call -----------------------------
2026-05-16T20:09:43.021039Z [info     ] Received pull_request event    _logger=<_FixedFindCallerLogger stampbot.webhook_handler (INFO)> _name=info extra={'event_type': 'pull_request', 'action': 'unlabeled'}
2026-05-16T20:09:43.021653Z [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-05-16T20:09:43.022169Z [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-05-16T20:09:43.021039Z'}
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-05-16T20:09:43.021653Z'}
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-05-16T20:09:43.022169Z'}
=========================== 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='140201004638832'>.call_count
 +    where <MagicMock name='github_client.dismiss_approval' id='140201004638832'> = <MagicMock name='github_client' id='140201004639840'>.dismiss_approval
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 169 passed, 3 deselected in 1.15s