92 lines
3.6 KiB
Markdown
92 lines
3.6 KiB
Markdown
---
|
|
id: TASK-47
|
|
title: 'Implement System Clock / Heartbeat Service for rSpace canvas'
|
|
status: To Do
|
|
assignee: []
|
|
created_date: '2026-02-18 22:30'
|
|
labels:
|
|
- feature
|
|
- infrastructure
|
|
- ecosystem
|
|
milestone: m-1
|
|
dependencies:
|
|
- TASK-43
|
|
references:
|
|
- rspace-online/backlog/tasks/task-43 - Implement-Event-Broadcasting-canvas-wide-pub-sub-system.md
|
|
- rSpace-website/docs/R-ECOSYSTEM-ARCHITECTURE.md
|
|
priority: high
|
|
---
|
|
|
|
## Description
|
|
|
|
<!-- SECTION:DESCRIPTION:BEGIN -->
|
|
Add a server-side System Clock service that broadcasts consistent time signals across the rSpace canvas via the Event Bus (TASK-43). This provides a shared temporal heartbeat that all shapes, modules, and connected r-ecosystem apps can subscribe to.
|
|
|
|
**Why**: Currently there is no shared sense of time passing in rSpace. Individual shapes have no way to trigger time-based behavior (recurring updates, countdowns, reminders, periodic recalculations). A system clock provides the foundational "pulse" that time-aware features depend on.
|
|
|
|
### Architecture
|
|
|
|
New file `server/clock-service.ts`:
|
|
- `SystemClock` class running server-side (not per-client)
|
|
- Emits events on the CanvasEventBus at configurable intervals:
|
|
- `clock:tick` — every 60 seconds (primary heartbeat)
|
|
- `clock:minute:5` — every 5 minutes
|
|
- `clock:hourly` — every hour
|
|
- `clock:daily` — at midnight UTC (configurable timezone)
|
|
- Payload includes: `{ timestamp, isoString, hour, minute, dayOfWeek, tickCount }`
|
|
- Single authoritative source — server emits, clients receive via CRDT sync
|
|
- Graceful degradation: if server is offline, clients can run a local fallback clock
|
|
|
|
### Shape Integration
|
|
|
|
Shapes opt in by subscribing to clock channels:
|
|
```typescript
|
|
// In a Budget shape
|
|
subscriptions: ['clock:hourly']
|
|
onEventReceived(channel: 'clock:hourly', payload) {
|
|
this.recalculate();
|
|
}
|
|
|
|
// In a Countdown shape
|
|
subscriptions: ['clock:tick']
|
|
onEventReceived(channel: 'clock:tick', payload) {
|
|
this.updateRemaining(payload.timestamp);
|
|
}
|
|
```
|
|
|
|
### Use Cases
|
|
|
|
- **Timer/Countdown shapes**: Update display every tick
|
|
- **Budget/Finance shapes**: Recalculate on hourly boundaries
|
|
- **Calendar shapes**: Highlight "now" marker, trigger reminders
|
|
- **Presence decay**: Mark users idle after N missed ticks
|
|
- **Backlog surfacing**: External services can subscribe to clock events to trigger task reviews
|
|
- **rTime.space foundation**: This clock becomes the temporal primitive that rTime.space builds on
|
|
- **Workflow triggers**: "Run this workflow every day at 9am"
|
|
|
|
### Configuration
|
|
|
|
Server-level config in community settings:
|
|
- `clock.enabled: true` (default true)
|
|
- `clock.timezone: 'UTC'` (for daily boundary)
|
|
- `clock.tickInterval: 60` (seconds, minimum 10)
|
|
- `clock.channels: ['tick', 'minute:5', 'hourly', 'daily']`
|
|
|
|
### Performance Considerations
|
|
|
|
- Clock events should NOT flood the CRDT eventLog — use a separate lightweight channel or mark as ephemeral (not persisted in ring buffer)
|
|
- Server-side only emission prevents N clients from emitting N duplicate ticks
|
|
- Tick events are fire-and-forget, no ack required
|
|
<!-- SECTION:DESCRIPTION:END -->
|
|
|
|
## Acceptance Criteria
|
|
<!-- AC:BEGIN -->
|
|
- [ ] #1 SystemClock emits `clock:tick` every 60s via CanvasEventBus
|
|
- [ ] #2 Configurable intervals: tick, 5-min, hourly, daily
|
|
- [ ] #3 Server-authoritative — only one clock source per canvas
|
|
- [ ] #4 Shapes can subscribe to clock channels and receive time payloads
|
|
- [ ] #5 Clock events are ephemeral (not persisted in CRDT eventLog ring buffer)
|
|
- [ ] #6 Fallback local clock when server connection is lost
|
|
- [ ] #7 Clock can be enabled/disabled per community in settings
|
|
<!-- AC:END -->
|