Outbound safety layer — as of PR #5285

Every reply Clara is about to send passes the policy registry before it leaves. A non-pass verdict never silences the conversation — substitute text ships instead, and the trace records what happened.

Inbound message SMS · email · voice · web Clara agent loop tools · context · Sonnet 4.6 Draft reply candidate text runOutboundSafetyChecks(reply, ctx) ctx = { capability, audience, channel, propertyId } · selects, sequences, times, traces — never decides Policy 1 · fair-housing LIVE — byte-identical to old gate appliesTo: possibly-leasing traffic fail direction: CLOSED Policy 2 · collections-legal OPEN SLOT — one file + one registry line appliesTo: capability === 'collections' ships with its own evals + stated fail dir then Order is semantic: each policy grades the text the previous one left behind — a rewrite from policy 1 is re-checked by policy 2. Skipped policies (appliesTo false) cost nothing. Every run emits a trace row: safety:fair-housing · status/blocked · detail · duration · billing lane via propertyId. pass text ships as written rewrite classifier's compliant rewrite ships instead block safe fallback copy ships; original text never leaves Send → recipient classifier error / timeout on a fail-closed policy = treated as non-pass → fallback ships, never the unchecked draft Not in registry collections legal-review (earlier pipeline stage) broadcast safety (advisory, fail-open)
PropFlow Docs