refactor(channels): extract access-policy gate from _handle_message_inner (#1026)#1036
Conversation
…nner (#1026) `_handle_message_inner` is a 398-line linear pipeline; the cross-channel access gate (#311, step 5b) was ~95 lines of group/DM branching inline and the dominant cyclomatic-complexity contributor (the function scored CC 65). Extract it into `_enforce_access_policy(...) -> (allowed, verified_email)`. The handler now calls the gate and short-circuits on `not allowed`. No behavior change — every branch (group any_verified unlock/lock, DM require_email prompt, DM verified-no-access pending request, open_access, legacy permissive) is preserved verbatim; deny paths still send their prompt/pending response before returning. Adds tests/unit/test_message_router_access_gate.py: 8 characterization tests driving the real `_handle_message_inner` through each access path (who reaches execute_task vs who is prompted/denied/queued). Green before and after the extraction. The security-critical gate had no isolated unit coverage before this. First decomposition step for `_handle_message_inner`; remaining phases (resolve/rate-limit/execute/finalize) are follow-up increments under #1026. Related to #1026 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Builds on the access-gate extraction: pull the remaining pipeline phases out of the 398-line handler into focused methods, leaving a thin linear orchestrator. Extracted: - _resolve_agent_and_token (steps 1–2) - _maybe_transcribe_voice (2b) - _enforce_rate_limits (3 + 3b) - _ensure_agent_available (4) - _run_agent_task (9 — execution params + failure/exception handling; returns result or None) - _finalize_response (10–14 — persist, MEM-001, outbound files, NO_REPLY, send, hooks, cleanup) Result: _handle_message_inner 398 → 112 lines, cyclomatic ~65 → ~10. Every early-return gate and the execute/finalize split preserve behavior exactly. Tests: extends test_message_router_access_gate.py to 16 characterization cases driving the real pipeline — entry-gate aborts (no agent, no token, rate-limited DM + group-silent-drop, container down, unverified), execution failure → error reply without finalize, and happy-path persist+send+hook. Green before and after each extraction; 152 message_router-adjacent unit tests pass. Note: handler is 112 lines (just over the 100-line target); the cyclomatic target (<20) is met. The remaining ~12-line step-7b file-upload glue is an optional final trim. Related to #1026 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
✅
|
|
Resolve by running |
vybe
left a comment
There was a problem hiding this comment.
Validated via /validate-pr: verbatim extraction of the #311 access gate (every branch + deny-path ordering preserved), now with net-new isolated coverage (8 characterization tests green pre+post). Clean refactor, green CI, no doc debt. Approving.
Summary
Second refactor from #1026 (after #1035 cleanup-sweeps). Targets the
message_router._handle_message_innerhotspot (391 lines, CC 65 per the refactor-audit)._handle_message_inneris a linear pipeline of ~14 steps. The cross-channel access gate (Issue #311, step 5b) was ~95 lines of group/DM branching inline — the dominant CC contributor and, notably, security-critical code with no isolated unit coverage. This extracts it into_enforce_access_policy(...) -> (allowed, verified_email); the handler now calls the gate and short-circuits onnot allowed.No behavior change. Every branch preserved verbatim:
any_verified: already-unlocked → proceed; sender verifies →set_group_verified+ proceed; nobody verified →prompt_group_auth+ abortnone: legacy permissiverequire_email+ no email →prompt_auth+ abortupsert_access_request+ pending reply + abort; open_access / legacy permissive → proceedDeny paths still send their prompt/pending response before returning.
verified_emailis returned for the downstream MEM-001 + session-source steps.Result
_handle_message_inner: 398 → 303 lines, the CC-65 branching block lifted out and isolated._enforce_access_policy: single-responsibility, ~CC 12.Tests
Adds
tests/unit/test_message_router_access_gate.py— 8 characterization tests that drive the real_handle_message_inner(fully-mocked adapter + patched collaborators) through every access path, asserting who reachesexecute_taskvs who is prompted/denied/queued. Green before and after the extraction — proof of behavior preservation, and net-new coverage on the #311 gate.Scope
Per the issue's "small incremental PRs — one function per PR" guidance, this is the first decomposition step for
_handle_message_inner. Remaining phases (resolve/token, rate-limit, availability, execute, finalize) are follow-up increments under #1026; the function is not yet under the <100-line / <20-CC target on its own — tracked for the next PRs.Related to #1026