SessionHandler Cohesion Split

Status: COMPLETE — shipped in commit e949984a (pushed and deployed with the F5 dispatch-reply hardening group; certifying gate11 rc=0, 30,790 / 0 / 0 / 0 at a 265 s test phase). Packets P1–P4 all landed: _session_kg.py (7 pure Cypher builder functions + SessionKgOps), _session_events.py (SessionEventOps), session.py shrunk 731→444 lines / 13 methods, with 87/87 parity tests green throughout every packet. One contract refinement vs the design: build_get_query kept the (kg_key, raw_id) signature — the contract diagram was synced to match. Design: plans/session-handler-split-design.md + plans/contracts/session-handler-split.mmd.

Why

Code review flagged gateway/handlers/session.py::SessionHandler as a god class: 607 lines (limit 200) and 24 methods (limit 10). Governing ruling: refactor by cohesion, not LOC — no new features, no schema changes, byte-identical behavior.

The cut

Safety rails (all held through landing)

Known defects (filed 2026-08-05)

Commit ac9b763f made the session-manager acceptance specs self-provisioning, eliminating the suite's 4 standing skips (164/164 executed, 0 skipped) — and in doing so surfaced two filed defects on the session RPC surface this split owns:

  1. Deployed fleet session.open is broken: the app container bind-mounts the checkout read-only, so session.open dies with PermissionError on .context.lock. Fix is deployment/config territory, filed.
  2. session.close has git-remote side effects: it calls SessionController.end(), which unconditionally git commit+tag+pushes against SESSION_ROOT — an RPC that pushes to a remote. A policy seam decision is filed (whether/when an RPC may run git side effects).

Repo pointers

plans/session-handler-split-design.md · plans/contracts/session-handler-split.mmd · src/orchestrata_ultimate/gateway/handlers/session.py (facade) · gateway/handlers/_session_kg.py · gateway/handlers/_session_events.py · implementation commit e949984a (precursor SET-2 helper pass: 9914d662; acceptance self-provisioning: ac9b763f).