# Phase-5 Anti-Silent / Group UX ## Goal Reduce user-facing silent outcomes in group/private chat flows while avoiding spam. ## Invariants - `I1`: no silent user-facing failure for `public_active` agents (`SILENT`/early-return -> short ACK). - `I2`: one-message rule per webhook in group chats. - `I3`: debounce by `(chat_id, agent_id, reason)` with cooldown. - `I4`: evidence in gateway event (`anti_silent_action`, `anti_silent_template`, `chat_type`). ## Deploy ```bash cd /opt/microdao-daarion docker compose -f docker-compose.node1.yml up -d --no-deps --build --force-recreate gateway ``` ## Seed / Precheck ```bash export GATEWAY_WEBHOOK_URL='http://127.0.0.1:9300/agromatrix/telegram/webhook' export PG_CONTAINER='dagi-postgres' pre_rows_gateway=$(docker exec "$PG_CONTAINER" psql -U daarion -d daarion_memory -tAc \ "SELECT count(*) FROM agent_experience_events WHERE source='gateway' AND ts > now()-interval '10 minutes';") pre_rows_join=$(docker exec "$PG_CONTAINER" psql -U daarion -d daarion_memory -tAc \ "SELECT count(*) FROM ( SELECT g.request_id FROM agent_experience_events g JOIN agent_experience_events r ON r.request_id=g.request_id WHERE g.source='gateway' AND r.source='router' AND g.ts > now()-interval '10 minutes' ) x;") pre_js=$(curl -sS http://127.0.0.1:8222/jsz?streams=true | python3 -c ' import json,sys j=json.load(sys.stdin) d=(j.get("account_details") or [{}])[0].get("stream_detail") or [] print(next((s.get("state",{}).get("messages",0) for s in d if s.get("name")=="EXPERIENCE"),0)) ') echo "pre_rows_gateway=$pre_rows_gateway" echo "pre_rows_join=$pre_rows_join" echo "pre_js=$pre_js" ``` ## Fixed Payload Replay Payloads: - `docs/ops/payloads/phase5_payload_group_unsupported_no_message.json` - `docs/ops/payloads/phase5_payload_group_source_lock.json` - `docs/ops/payloads/phase5_payload_private_photo_unsupported.json` ```bash # 1) group unsupported_no_message curl -sS -X POST "$GATEWAY_WEBHOOK_URL" \ -H 'content-type: application/json' \ -d @docs/ops/payloads/phase5_payload_group_unsupported_no_message.json # 2) group source_lock pair (same update_id) curl -sS -X POST "$GATEWAY_WEBHOOK_URL" \ -H 'content-type: application/json' \ -d @docs/ops/payloads/phase5_payload_group_source_lock.json curl -sS -X POST "$GATEWAY_WEBHOOK_URL" \ -H 'content-type: application/json' \ -d @docs/ops/payloads/phase5_payload_group_source_lock.json # 3) private photo unsupported (photo follow-up without image context) curl -sS -X POST "$GATEWAY_WEBHOOK_URL" \ -H 'content-type: application/json' \ -d @docs/ops/payloads/phase5_payload_private_photo_unsupported.json ``` ## Assertions ### A) Strict row delta ```bash post_rows_gateway=$(docker exec "$PG_CONTAINER" psql -U daarion -d daarion_memory -tAc \ "SELECT count(*) FROM agent_experience_events WHERE source='gateway' AND ts > now()-interval '10 minutes';") delta_rows=$((post_rows_gateway-pre_rows_gateway)) echo "delta_rows=$delta_rows" # expected: delta_rows == 4 ``` ### B) Anti-silent evidence in DB ```bash docker exec "$PG_CONTAINER" psql -U daarion -d daarion_memory -P pager=off -c " SELECT request_id, raw->>'chat_type' as chat_type, raw->'policy'->>'sowa_decision' as sowa, raw->'policy'->>'reason' as reason, raw->>'anti_silent_action' as anti_silent_action, raw->>'anti_silent_template' as anti_silent_template, raw->'result'->>'http_status' as http_status, ts FROM agent_experience_events WHERE source='gateway' AND agent_id='agromatrix' AND ts > now()-interval '10 minutes' ORDER BY ts DESC LIMIT 25; " # expected: # - reason=unsupported_no_message with sowa=UNKNOWN (group path) # - reason=source_lock_duplicate_update with anti_silent_action in (ACK_EMITTED, ACK_SUPPRESSED_COOLDOWN) # - reason=photo_followup_without_image_context with anti_silent_action=ACK_EMITTED ``` ### C) Metrics ```bash curl -sS http://127.0.0.1:9300/metrics | grep -E \ 'gateway_anti_silent_total|gateway_ack_sent_total|gateway_experience_emitted_total|gateway_early_return_total' # expected: # - gateway_anti_silent_total{action="ACK_EMITTED",reason="...",chat_type="group|private"} increments # - source-lock repeated request may produce ACK_SUPPRESSED_COOLDOWN depending on timing ``` ### D) Join sanity (normal path still healthy) ```bash post_rows_join=$(docker exec "$PG_CONTAINER" psql -U daarion -d daarion_memory -tAc \ "SELECT count(*) FROM ( SELECT g.request_id FROM agent_experience_events g JOIN agent_experience_events r ON r.request_id=g.request_id WHERE g.source='gateway' AND r.source='router' AND g.ts > now()-interval '10 minutes' ) x;") echo "post_rows_join=$post_rows_join" # expected: non-zero with normal traffic ``` ## PASS Criteria - `delta_rows == 4` for fixed replay batch. - Deterministic reason codes present (`unsupported_no_message`, `source_lock_duplicate_update`, `photo_followup_without_image_context`). - `anti_silent_action` is present for anti-silent branches (`ACK_EMITTED` or `ACK_SUPPRESSED_COOLDOWN`). - No evidence of double-event for one webhook request.