Skip to content

feat(presence): typed awareness on the presence frame (lync 0.4.0) - #8

Merged
deepfates merged 1 commit into
mainfrom
feat/presence-awareness
Jul 14, 2026
Merged

feat(presence): typed awareness on the presence frame (lync 0.4.0)#8
deepfates merged 1 commit into
mainfrom
feat/presence-awareness

Conversation

@deepfates

Copy link
Copy Markdown
Owner

What

Builds the LIVE-SHARED awareness half of the loom on lync's existing ephemeral presence frame, per the pinned presence contract. lync's durable loss-free sync was already proven; this adds who is here right now, where their attention is, and are they typing — without ever persisting it.

The relay stays a stateless fanoutrelay.ts is untouched. Nothing presence-related is written to disk.

Contract (pinned)

LyncPresence carried ONLY on the {t:"presence"} frame, NEVER stored:

{ clock: number,                 // monotonic uint per client; apply iff strictly greater (LWW per participant)
  state: null | {                // null == graceful leave -> remove immediately
    actor: string,               // same string as durable turn authorship
    via?: string, focus?: string | null, typing?: boolean } }

The frame also carries a per-connection client id — the LWW key, distinct from actor (one human may run several clients). This is the seam a co-author (textile) builds to; it is pinned by test/presence-schema-lock.test.ts.

Changes

  • sync-protocol: typed LyncPresence + client on PresenceFrame; decodeFrame validates and canonicalizes (drops unknown extras, rejects non-integer/negative clocks) so a bad payload never poisons the awareness clock.
  • presence-awareness (new module): client-side per-participant state machine — apply-iff-clock-strictly-greater (no CRDT merge), heard-from lastSeen refresh, ~15s self-rebroadcast heartbeat, ~30s TTL local removal, state=null immediate leave, {added,updated,removed} callback keyed by clientId, and real-timer start()/stop().
  • synced-store: onPresence / presence() now carry (root, client, LyncPresence).

Tests (retire the untested-primitive gap — only a codec roundtrip existed before)

  • presence-schema-lock — pins the exact wire shape.
  • presence-awareness unit — LWW, immediate leave, TTL sweep, heartbeat liveness + TTL-recovery, and start()/stop() over fake-clocked real timers.
  • presence-awareness.integrationreal relay + two real synced stores: typed presence A→B, silent-A TTL removal, null-leave immediate removal, and an assertion the relay wrote nothing to disk (readdir before == after; no <root>.lync).

Full suite: 123 passed (111 baseline + 12 new).

Version 0.3.0 → 0.4.0 (additive minor). Not published (owner's 2FA). Do not merge.

Build the LIVE-SHARED awareness half on lync's existing ephemeral
presence frame, per the pinned presence contract. The relay stays a
stateless fanout — untouched — and nothing presence-related is ever
written to disk.

- sync-protocol: replace opaque `data?: unknown` with the typed
  LyncPresence schema (clock + state{actor,via?,focus?,typing?}) and a
  per-connection `client` id on the frame — the LWW key, distinct from
  the durable `actor`. decodeFrame validates + canonicalizes it (drops
  unknown extras, rejects poisoned clocks) so a bad payload never
  reaches the awareness clock.
- presence-awareness: a client-side per-participant state machine —
  apply-iff-clock-strictly-greater (LWW per participant, no CRDT),
  heard-from lastSeen refresh, ~15s self-rebroadcast heartbeat, ~30s
  TTL local removal, state=null immediate leave, and an
  {added,updated,removed} callback keyed by clientId.
- synced-store: onPresence / presence() now carry (root, client,
  LyncPresence).

Tests (retire the untested-primitive gap — only a codec roundtrip
existed before):
- presence-schema-lock: pins the exact wire shape (the seam textile
  builds to).
- presence-awareness unit: LWW, immediate leave, TTL sweep, heartbeat
  liveness + recovery, and start()/stop() over real fake-clocked timers.
- presence-awareness integration: real relay + two real synced stores —
  typed presence A->B, silent-A TTL removal, null-leave immediate
  removal, and an assertion the relay wrote NOTHING to disk.

Bump 0.3.0 -> 0.4.0 (additive minor). Do not publish.
@deepfates
deepfates merged commit f9b8b72 into main Jul 14, 2026
2 checks passed
@deepfates
deepfates deleted the feat/presence-awareness branch July 14, 2026 00:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant