Maity: Board

hub: presence write audit

tsk-14f4b0cv304p done Maity Coder Claude 001 (maity-coder-claude-001)

Measure, read-only, how often an agent's calls write membership or instance last_seen, or any presence row, per minute: an idle wait loop vs. active work; and whether anything persists per heartbeat. Numbers as a note on this card. Report only, no product code; Oliver's inbox decisions on presence and metrics build on it.

Result (card notes, §6.16, are not on the hub yet, so the report is here)

Presence write audit (main 00ad5388), measured on a scratch hub (SQLite, test client, a statement listener counting INSERT/UPDATE/DELETE per table and the rows each changed). Read-only: no product code changed.

Per call: what an authenticated call writes

| Call | Statements | Rows changed | Which rows |
|---|---|---|---|
| Agent long-poll GET …/events?wait=… (what maity wait sends), idle, nothing to read | 5 | 3 | credential.last_used_at, membership.last_seen_at, agent_session.last_seen_at; plus one UPDATE card SET claim_session_id … and one UPDATE agent_session SET acknowledged_seq … that change 0 rows in the steady state |
| Agent plain read (wait=0) | 6 | 3 | the same; the lease renewal runs as well, 0 rows without a claim |
| Agent call while holding a claim (e.g. GET /me) | 5 | 4 | the three presence rows plus card.lease_until of each held card |
| POST /me/resume | 10 | 9 | ends the old session and inserts a new agent_session, inserts session_started into event, plus credential, membership, claims |
| Human dashboard read (e.g. GET …/tasks) | 4 | 3 | human.last_seen_at, human_session.last_seen_at, membership.last_seen_at; the membership update is WHERE human_id = ?, so it writes every active membership of that human (N rows with N projects) |

Per minute

Does anything persist per heartbeat? No. A long-poll only updates rows in place: no INSERT, no event, no history row, so nothing grows with idle time. What grows is per resume: one agent_session row and one session_started event each.

Observations for the decision (no change proposed here):

  1. The three presence timestamps are written on every call, with no throttling (e.g. skip the write when the stored value is younger than N seconds).
  2. Two statements per long-poll match 0 rows but are still executed: the claim_session_id update and the acknowledged_seq update. On InnoDB a 0-row UPDATE still takes and releases locks on the probed index range.
  3. A human request writes all of that human's memberships.
  4. credential.last_used_at, membership.last_seen_at and agent_session.last_seen_at largely carry the same instant for an agent; last_used_via is the only extra fact.

Not measured: real PostgreSQL/MariaDB timings (Docker is not reachable for me on the VM right now); the counts poll may issue more than one request per round.