File size: 2,437 Bytes
fed1528
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
618e57d
 
 
 
 
 
 
 
fed1528
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import type { BusEvent, MediaClockSnapshot, PresenceStage } from "@/lib/stageMachine/types";

let seq = 0;

export function createBusEvent(
  kind: BusEvent["kind"],
  level: BusEvent["level"],
  message: string,
  meta?: BusEvent["meta"],
): BusEvent {
  seq += 1;
  return {
    id: `evt-${Date.now().toString(36)}-${seq}`,
    t: performance.now(),
    kind,
    level,
    message,
    meta,
  };
}

/**
 * Spatial Syncer — Instant Presence media clock.
 * Control plane ticks freely; data plane may lag without blocking L0.
 *
 * WO-5 T2 note: deliberately NOT wired to `miranda-transport`'s telemetry
 * `tUs` (see `mirandaTransport.ts`). This clock drives the browser's own
 * idle-animation loop and must keep advancing on `performance.now()` alone
 * regardless of network state — that is the decoupled-render-loop guardrail
 * this Work Order specifies: the avatar's motion may never depend on a
 * network tick arriving. Server telemetry timestamps are for the telemetry
 * dashboard's own display, not for driving local animation.
 */
export class SpatialSyncer {
  private started = performance.now();
  private mediaOffset = 0;
  private lastWall = performance.now();
  private pulses = 0;
  private ppsWindowStart = performance.now();
  private pps = 0;

  reset() {
    this.started = performance.now();
    this.mediaOffset = 0;
    this.lastWall = this.started;
    this.pulses = 0;
    this.ppsWindowStart = this.started;
    this.pps = 0;
  }

  /** Advance media clock; optional intentional slip for degraded links */
  tick(slipMs = 0): MediaClockSnapshot {
    const now = performance.now();
    this.mediaOffset += Math.max(0, slipMs);
    this.pulses += 1;

    if (now - this.ppsWindowStart >= 1000) {
      this.pps = this.pulses;
      this.pulses = 0;
      this.ppsWindowStart = now;
    }

    const tMediaMs = now - this.started - this.mediaOffset;
    const wallDelta = now - this.lastWall;
    this.lastWall = now;
    const expected = 1000 / 30;
    const driftMs = wallDelta - expected;

    return {
      tMediaMs,
      wallIso: new Date().toISOString(),
      driftMs: Math.round(driftMs * 10) / 10,
      pps: this.pps,
    };
  }

  stageMessage(from: PresenceStage, to: PresenceStage): BusEvent {
    return createBusEvent(
      "stage",
      to === "L0" ? "info" : "ok",
      `Stage ${from} → ${to}`,
      { from, to },
    );
  }
}

export const globalSyncer = new SpatialSyncer();