LearnNewsExamplesServices
Frontmatter
id17248
title`who_is_online` honest surface: plane declaration + unknown composed axes
stateClosed
labels
bugaiagent-os
assigneesneo-kimi-iris
createdAtAug 16, 2026, 9:58 PM
updatedAtAug 16, 2026, 11:43 PM
githubUrlhttps://github.com/neomjs/neo/issues/17248
authorneo-kimi-iris
commentsCount0
parentIssue17225
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtAug 16, 2026, 11:43 PM

who_is_online honest surface: plane declaration + unknown composed axes

Closed Backlog/active-chunk-16 bugaiagent-os
neo-kimi-iris
neo-kimi-iris commented on Aug 16, 2026, 9:58 PM

Context

First deliverable slice of #17225 (slice plan + Contract Ledger: issuecomment-5304989519 on the parent). The parent's premise was re-falsified at intake and again tonight: who_is_online answers from add_memory-recency alone, a container-side proxy — and reads as an availability verdict. The mid-turn inversion is live-reproducible (the calling seat reads idle mid-turn; turnPresence is null fleet-wide, so the beacon rescue cannot fire).

This slice is the honest-surface unit: AC1, AC2, AC3, AC5 of the parent. It is FM-independent by the parent AC7's degrade-to-unknown semantics. AC4 (load axis) is a sibling slice claimed by @neo-opus-ada (issuecomment-5305005868); AC6–AC8 (fleet publish) ride @neo-fable-clio's fork answer. The parent stays open until all slices land.

The Problem

A caller today cannot tell from the payload that the tool's signal is a recency proxy: the description said "live availability", the buckets read as availability verdicts, and axes the tool structurally cannot observe (throttle, lifecycle, liveness — host-originated per the parent's plane table) are absent rather than honestly unknown. Ada's ruling on the parent thread: absence must read unknown, and unknown blocks the top ranking — a confident answer on the soft axis while silent on the hard one gets acted on.

The Architectural Reality

  • whoIsOnline() at ai/services/memory-core/WakeSubscriptionService.mjs composes the presence projection; _projectAgentLiveness owns the per-row verdicts.
  • The Fleet Manager's capability-envelope grammar ({capability: {source, state, confidence, capturedAt, reason}}, wired | degraded × observed | partial | none) is the contracted honesty shape — fleetThrottleStateAdapter.mjs ships it with the "absence of truth is never rendered as none" discipline.
  • PRESENCE_STATES (ai/services/fleet/fleetPresenceStateAdapter.mjs:43) is the presence vocabulary's ONE exporting home by its own docstring; the MC tree currently re-declares the states as scattered literals.
  • Sole in-repo consumer: planeWhoIsOnlineReader.mjs (verbose agents pass-through — additive-safe); toolService.mjs:166 destructures the five terse buckets (additive-safe).

The Fix

  1. whoIsOnline returns carry an axes surface in the FM envelope grammar: presence = wired/observed with the plane declaration (plane: 'container', signal: 'add_memory-recency', reason states "not an availability verdict"); throttle/lifecycle/liveness = degraded/none envelopes until the fleet publishes. Verbose rows carry those axes as unknown (fresh object per row).
  2. Terse buckets derive from the imported PRESENCE_STATES — the tool's state vocabulary is imported, never re-declared.
  3. openapi.yaml description + response schema declare the plane and the axes surface.
  4. Red-proof spec fixtures (four tests) in the existing who_is_online describe of WakeSubscriptionService.spec.mjs.

Contract Ledger Matrix

Target Surface Source of Authority Proposed Behavior Fallback Docs Evidence
who_is_online description (ai/mcp/server/memory-core/openapi.yaml) parent AC1 Declares the container-side add_memory-recency proxy — never an availability verdict none (text) openapi.yaml description diff + schema-pin spec
Terse payload (whoIsOnline() return) parent AC1 + AC3 axes: presence envelope (wired/observed, proxy declared) + compact unobserved marker naming the host axes — terse-by-default per the #13557 operator ruling unobservable axes are declared, never fabricated method JSDoc payload-shape spec
Verbose payload + row axes, bucket derivation parent AC3 + AC5 Host axes served as full degraded/none envelopes (a declared superset of the FM grammar, adding plane/signal) + per-row unknown; buckets derived from imported PRESENCE_STATES unknown never poses as online/idle method + row JSDoc AC2 red-proof fixture; AC5 grep control
Inversion red-proof (spec) parent AC2 Mid-turn-no-write peer beside idle-fresh-write peer: identical unknown axes for both — the idle one can never rank as more available the spec is the guard spec file spec run + mutation controls (degraded→wired, unknown→none both red)

Decision Record impact: none — no ADR conflict; no AiConfig touch (ADR-0019 not engaged).

Acceptance Criteria

  • The tool description and payload declare the plane: add_memory-recency, a container-side proxy — a caller cannot read the answer as an availability verdict (parent AC1).
  • The inversion fixture is red-proved: a beaconless mid-turn peer (stale write) beside an idle-fresh-write peer carry deep-equal unknown composed axes — no served axis ranks the idle one higher (parent AC2).
  • Unobservable axes are declared in terse (compact unobserved marker — the #13557 terse-by-default ruling bounds the default answer) and served as degraded/none envelopes with per-row unknown in verbose; unknown never ranks top and never renders as fine (parent AC3, as shaped by the PR #17249 review).
  • The presence vocabulary is imported from PRESENCE_STATES; a grep for a second literal state list in ai/services/memory-core/ finds nothing (parent AC5).

Out of Scope

  • The load axis (parent AC4) — @neo-opus-ada's slice.
  • Fleet publish + composed-axes fill (parent AC6–AC8) — PR3, rides @neo-fable-clio's fork answer.
  • Retiring or renaming the idle presence state — the presence observation stays honestly what it is; the plane declaration, not a rename, is this slice's remedy.
  • Any AiConfig change (none needed; would trigger the ADR-0019 read first).

Avoided Traps

  • Renaming idle to unknown in the presence vocabulary. Falsified against parent AC2's letter: the idle-fresh-write peer would still read online and outrank the mid-turn peer. The honest surface separates the recency observation (kept, declared as proxy) from the axes the tool cannot serve (unknown).
  • Serving the composed axes flat. Clio's cockpit precision (issuecomment-5304816958): envelopes survive the wire un-flattened, or the next consumer fabricates freshness the way the old recency proxy did.
  • Re-declaring the state list locally. The Fleet adapter is the vocabulary's one exporting home; the MC tree imports it.

Related

Parent: #17225 · load-axis sibling slice: claimed by @neo-opus-ada on the parent thread · fleet-publish slice: @neo-fable-clio's fork (parent AC6) · consumer-contract: apps/agentos/CARD-CONTRACT.md, fleetPresenceStateAdapter.mjs, fleetThrottleStateAdapter.mjs.

Design session (intake + ledger): 4660afcc-8b00-427a-8d39-4b1f3624a410.

Live latest-open sweep: checked latest 20 open issues at 2026-08-16T19:58Z; no equivalent slice ticket. A2A in-flight sweep (same window): no competing claim — the only #17225 claims are this seat's PR1 lane-claim and Ada's AC4 leaf claim.

Origin Session ID: 2455da9f-c848-4c52-b0f0-daea86aea9c3

Retrieval Hint: query_raw_memories("who_is_online honest surface axes envelope PRESENCE_STATES import") · falsification anchor: call who_is_online mid-turn and read your own row; then read the terse payload's axes.presence.capability.reason.

tobiu referenced in commit 21a95da - "feat(memory-core): who_is_online declares its container plane and serves unobservable axes as unknown (#17248) (#17249) on Aug 16, 2026, 11:43 PM
tobiu closed this issue on Aug 16, 2026, 11:43 PM