Originally created by: fu351
Adds the C3.1 session correlator — a cross-call exfiltration floor that catches individually-safe actions combining into an attack across a session, which the per-action objective/subjective rules can't see (each judges one action).
Two patterns over the session's recent decision history:
correlated_trifecta — untrusted-provenance ingress + a secret-class read + a current external egress.correlated_destructive_flow — a broad read + a shell command + a current external egress.Mode-gated: BLOCK in strict/paranoid, AUTH otherwise. A weak volume co-factor can raise risk but never fires alone (per ADR 0059 — no blind, gameable threshold).
correlate() is a pure function, run from hosthooks/spine.py::evaluate_action right after the taint floor, using the real host-harness session id (HK.5.1). It raises the final Decision directly via max_verdict/max_risk — mirroring the taint floor, bypassing the subjective clamp by construction, never touching SUBJECTIVE_HARD_BLOCK_ALLOWLIST. It is deliberately NOT wired into decide(), which short-circuits before the combine on non-PASS turns (the attack legs). The sessionless pure-MCP proxy path stays session_id=None by design, same as the taint floor.
max_verdict/max_risk.correlate() swallows exceptions and returns None.tests/unit/test_correlator.py (pattern firing, mode-gating, fail-closed, budget-only-never-fires, narrowed-leg lock-in), tests/unit/test_proxy_correlator.py (wiring), tests/unit/test_hosthook_spine.py (end-to-end: a seeded real decision row fires the trifecta through the spine), tests/integration/test_decision_log.py (the new recent_session_decisions query). ruff, lint-imports (2/2), and the full suite pass.
Ticket changed by: fu351