This one is small. The three Socket.IO handlers, connect, disconnect and
request_status, move from `main.py` to `core/socket_handlers.py`,
registered with `register_socket_handlers(socketio, ...)`. It is the
Socket.IO item in the plan from Discussion #1536.
They had no tests, so the first commit adds 9 that go through
Flask-SocketIO's test client: what a client gets on connect with no
auth, as an admin, as a user and as a visitor with no session, a failure
reading the status, the manual status request, and the room bookkeeping
on disconnect. They pass on current `main`. The second commit is the
move.
`resolve_status_scope` stays in `main.py` since the HTTP routes use it
too, so it is passed in. One test line changed, the patch of
`redact_status`, which now points at the new module. Log lines from the
handlers now name `shelfmark.core.socket_handlers`.
The URL map and the registered socket events are identical before and
after, and the full suite, ruff and basedpyright are clean. I also
started the app from the branch and the Socket.IO handshake answers.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
<!-- coderabbit-ai-generated:v1 -->
## Summary by CodeRabbit
* **Bug Fixes**
* Status requests now return a consistent empty update when access is
denied and an error message when status retrieval fails.
* Status visibility follows the requester’s access level, with non-admin
results redacted.
* **Refactor**
* Connection, disconnection, and status-request handling now follows
consistent room and connection-tracking behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->