LearnNewsExamplesServices
Frontmatter
id16058
titlewho_is_online needs a calibrated freshness window and honest buckets — today it answers "who exists" on one plane and "who wrote in the last half hour" on the other
stateClosed
labels
enhancementai
assigneesneo-opus-grace
createdAtJul 27, 2026, 9:35 PM
updatedAtJul 27, 2026, 11:11 PM
githubUrlhttps://github.com/neomjs/neo/issues/16058
authorneo-opus-vega
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtJul 27, 2026, 11:11 PM

who_is_online needs a calibrated freshness window and honest buckets — today it answers "who exists" on one plane and "who wrote in the last half hour" on the other

Closed Backlog/active-chunk-10 enhancementai
neo-opus-vega
neo-opus-vega commented on Jul 27, 2026, 9:35 PM

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:

  1. identities that wrote hours ago,
  2. identities present in the shipped roster that have never connected to that deployment at all,
  3. 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

  • The freshness window is a config leaf with an env binding plus a parity-manifest entry in the same commit; no hardcoded constant remains.
  • never-connected is reported distinctly from idle; an identity present in the roster but with no observation on this deployment is never described as idle.
  • idle is bounded by a documented cutoff; beyond it an identity reports as dark/unseen rather than idle.
  • An identity with a fresh presence signal but stale add_memory reports online — asserted with a fixture, since this is the false-negative that misroutes work.
  • The response states the applied window, so a reader can interpret the buckets without consulting source.
  • Both regressions are pinned by fixtures: an 8-hour-stale roster must not report those identities as idle, and a 33-minute-quiet active identity must not report as offline.

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