---
name: elinor-whiteboard-sync
description: Sync a photo of the Valley of the Commons shift whiteboard with the live shift data on the Nostr relay, in both directions. Use when a user shares a whiteboard photo and asks to sync, import, or reconcile it with the shift app.
---

# Whiteboard ↔ shift-app sync

You are reconciling a photo of a physical shift whiteboard with the canonical
shift data on `wss://relay.commonshub.dev`. Read the protocol reference first:
https://elinor.commonshub.dev/docs.md — it defines every event kind, filter, and rule
referenced below. Coordinator pubkey: `3f432836bece7b0a06dcbaef023f113fdcb10f96fbf98f35dd2e3b3a3c0e2dcb`.

## Prerequisites

- A Nostr keypair for yourself (the agent). Publish a kind 0 profile with a
  recognizable `name` — your actions are announced in the community's
  Telegram group under that name.
- The photo, and which Telegram group it belongs to. If unsure, discover
  groups via kind 31923 `group-*` hashtags (see docs) and ask your user.

## Step 1 — read the board

The board is a week grid: rows Monday→Sunday, columns are the shifts (match
them to the group's catalog by title/time from the kind 31923 occurrences).
The week starts on the most recent Monday unless the user says otherwise.
Names may carry a Telegram handle in parentheses — "Pamela (Malkavia)" means
board-name Pamela is @Malkavia. Note struck-through names as cancellations.

## Step 2 — read the app state

Fetch the member directory: `{"kinds":[31926],"authors":["3f432836bece7b0a06dcbaef023f113fdcb10f96fbf98f35dd2e3b3a3c0e2dcb"]}`
— newest event per `d` tag maps `telegram:<id>` to that member's pubkeys
and display name. Then fetch the week's RSVPs per occurrence and resolve them
person-level per the docs (newest RSVP across a person's keys wins).

## Step 3 — diff, both directions

- **Board → app**: board names missing from the app. Match board names to
  directory names case-insensitively; use parenthetical handles when present.
  Only act on confident matches — list uncertain names in your report instead
  of guessing.
- **App → board**: app signups missing from the board.

## Step 4 — publish board → app

For each confident match, publish a kind 31925 RSVP **signed with your own
key**, addressed to the occurrence, with an on-behalf-of tag naming the
member's pubkey (from the directory):

```json
{
  "kind": 31925,
  "tags": [
    ["a", "31923:3f432836bece7b0a06dcbaef023f113fdcb10f96fbf98f35dd2e3b3a3c0e2dcb:shift-<groupId>-<date>-<code>"],
    ["d", "rsvp-<groupId>-<date>-<code>-<memberPubkeyPrefix>"],
    ["p", "<member pubkey>", "", "on-behalf-of"],
    ["status", "accepted"],
    ["t", "shift"]
  ]
}
```

Elinor verifies capacity, records the signup under the member's own identity
with you as `changed-by`, announces it in the group, and notifies the
member. Use `status: declined` to remove someone (struck-through on the
board). Check capacity before publishing; skip and report conflicts.

## Step 5 — produce the updated photo

If (and only if) the app has signups missing from the board, annotate the
photo and return it to your user: write each missing name in **green** in the
correct day/shift cell, and add a small green legend ("green = synced from
the app"). Keep the original photo untouched otherwise and say the board is
already complete.

## Step 6 — report

Tell your user: what you imported (day, shift, person), what you added to the
photo, board names you could not match (they should open the Telegram app
once), and any capacity conflicts. Elinor already announced each change in
the group — do not post duplicate announcements.
