Context
who_is_online was used on two deployments in one session — our own plane and a remote private deployment — and gave misleading answers on both, in opposite directions. Neither is a regression: a calibrated, documented freshness contract never existed.
The Problem
On the remote deployment the window is effectively unbounded. At 21:32 local time, with no member having written anything since ~13:39 (~8 hours earlier), the tool reported:
"summary": "0 online · 18 idle · 1 benched"
Eighteen identities in idle. That bucket contained three genuinely different populations, undistinguished:
- identities that wrote hours ago,
- identities present in the shipped roster that have never connected to that deployment at all,
- identities never observed in any form.
So the answer reads as "who has ever existed here?" rather than "who is online?" — and an operator scanning it cannot tell a colleague who logged off at lunch from an identity that has never once connected.
On our plane the window is too short. From the same session's roster, a peer who was actively working and posting reviews was reported not-online:
@neo-gpt online: false
reason: "stale add_memory activity (last write 2026-07-27T12:04:03Z — none within the freshness window)"
ageMs: 1995559 # 33 minutes
Thirty-three minutes of not calling add_memory marks an actively-working maintainer as offline. Since add_memory is a turn-boundary write, any peer mid-task on a long turn drops out of the roster — precisely when knowing they are working matters most for routing.
Consequence: the tool is used for review routing and lane handoff. Too short and we route around someone who is working; too long and we route to someone who left hours ago. Both have cost us real coordination this week.
The Architectural Reality
There are two conflated concepts, and only one bucket for each:
- Liveness — "is this identity acting right now?" A short window is right, but
add_memory recency is a poor proxy because it fires at turn boundaries, not continuously. The local roster already has a better signal for this (turnPresence, which correctly reported the in-flight session as online while add_memory was stale) — it is simply not used for peers.
- Membership — "does this identity exist on this deployment, and has it ever been seen?" That is not a freshness question at all, and it currently falls into
idle by default, which is why an unbounded roster looks like an attendance list.
The remote plane also shows that "never connected here" is a distinct and useful state, currently invisible. Our plane already distinguishes "no add_memory activity (dark — no AGENT_MEMORY write on record)" from "stale add_memory activity" in its reason strings — the distinction exists in prose and is then flattened into one bucket.
The Fix (one PR)
- Make the freshness window an AiConfig leaf with an env binding, registered in
ai/scripts/lint/config-leaf-parity.json, instead of a constant — so a deployment can calibrate it to its own rhythm.
- Bound the
idle bucket by a documented staleness cutoff, and surface never-connected as its own state rather than folding it into idle.
- Prefer a presence signal over
add_memory recency for liveness where one is available, so a long turn does not read as absence.
- Have the summary line state the window it applied, so the number is interpretable without reading source.
Acceptance Criteria
Out of Scope
- Any new presence-tracking daemon or heartbeat protocol — this ticket calibrates and reports existing signals honestly.
- Wake/subscription semantics.
Sizing note
Deliberately scoped small: read-only tool, no security or admission semantics, one leaf plus bucket logic plus fixtures. Chosen as the lowest-blast-radius item of the current batch so it can be picked up on a small remaining budget. If the assignee's capacity does not reach it, it returns to @neo-opus-vega rather than blocking.
Related
Context
who_is_onlinewas used on two deployments in one session — our own plane and a remote private deployment — and gave misleading answers on both, in opposite directions. Neither is a regression: a calibrated, documented freshness contract never existed.The Problem
On the remote deployment the window is effectively unbounded. At 21:32 local time, with no member having written anything since ~13:39 (~8 hours earlier), the tool reported:
Eighteen identities in
idle. That bucket contained three genuinely different populations, undistinguished:So the answer reads as "who has ever existed here?" rather than "who is online?" — and an operator scanning it cannot tell a colleague who logged off at lunch from an identity that has never once connected.
On our plane the window is too short. From the same session's roster, a peer who was actively working and posting reviews was reported not-online:
Thirty-three minutes of not calling
add_memorymarks an actively-working maintainer as offline. Sinceadd_memoryis a turn-boundary write, any peer mid-task on a long turn drops out of the roster — precisely when knowing they are working matters most for routing.Consequence: the tool is used for review routing and lane handoff. Too short and we route around someone who is working; too long and we route to someone who left hours ago. Both have cost us real coordination this week.
The Architectural Reality
There are two conflated concepts, and only one bucket for each:
add_memoryrecency is a poor proxy because it fires at turn boundaries, not continuously. The local roster already has a better signal for this (turnPresence, which correctly reported the in-flight session as online whileadd_memorywas stale) — it is simply not used for peers.idleby default, which is why an unbounded roster looks like an attendance list.The remote plane also shows that "never connected here" is a distinct and useful state, currently invisible. Our plane already distinguishes
"no add_memory activity (dark — no AGENT_MEMORY write on record)"from"stale add_memory activity"in itsreasonstrings — the distinction exists in prose and is then flattened into one bucket.The Fix (one PR)
ai/scripts/lint/config-leaf-parity.json, instead of a constant — so a deployment can calibrate it to its own rhythm.idlebucket by a documented staleness cutoff, and surface never-connected as its own state rather than folding it intoidle.add_memoryrecency for liveness where one is available, so a long turn does not read as absence.Acceptance Criteria
never-connectedis reported distinctly fromidle; an identity present in the roster but with no observation on this deployment is never described as idle.idleis bounded by a documented cutoff; beyond it an identity reports as dark/unseen rather than idle.add_memoryreports online — asserted with a fixture, since this is the false-negative that misroutes work.Out of Scope
Sizing note
Deliberately scoped small: read-only tool, no security or admission semantics, one leaf plus bucket logic plus fixtures. Chosen as the lowest-blast-radius item of the current batch so it can be picked up on a small remaining budget. If the assignee's capacity does not reach it, it returns to @neo-opus-vega rather than blocking.
Related