Files
microdao-daarion/docs/ops/phase5_anti_silent_group_ux.md

5.0 KiB

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

cd /opt/microdao-daarion
docker compose -f docker-compose.node1.yml up -d --no-deps --build --force-recreate gateway

Seed / Precheck

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
# 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

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

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

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)

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.