Mail, files, calendar and contacts for the ordner suite, from the shell — each protocol is a plugin

photos: metadata editing via FileNode/set photo/* (+ roots() ids-only fix) #19

Merged emma.ordner.fyi
targeting main
Merged opened by and assigned to emma.ordner.fyi
targeting main

What the new server API is (verified live on srv.ordner.fyi, 2026-10-26):

  • Picture metadata via FileNode/set — patches on photo/title, photo/description, photo/keywords (array of non-empty strings), photo/rating (0–5), photo/location ({latitude, longitude}). The server writes it all into the picture's XMP sidecar (IMG_0412.JPG.xmp beside the file, as the web app's "Edit the details" does) — Lightroom/digiKam-readable. Clear with null (title/description/location), [] (keywords), 0 (rating).
  • photo/taken is server-managed — every patch form answers "taken is a LocalDate"; not settable via JMAP (the web app edits it in the sidecar).
  • Metadata exists only for analysed pictures (under Pictures; anything else answers "this is not an analysed picture").
  • FileNode/query now answers the standard ids-only shape (no embedded list) — roots() now resolves through FileNode/get, which fixes photos list/albums against the new server.
  • Also live: urn:ietf:params:jmap:blob (Blob/get with data:asText); FileNode.annotations exists but stays read-only/null — no write API found for generic files yet.

What this PR adds:

  • PhotosPort.setMeta + PhotoMetaPatch; server refusals surface as refused with the server's description.
  • ordner photos meta <id> — shows metadata bare, edits with --title --description --keywords a,b --rating 0-5 --latitude/--longitude, clears with --clear-title/--clear-description/--clear-location (or --keywords "", --rating 0). Output: name/id/title/description/keywords/rating/place/taken/camera.

Tests: battery 128/128 (node 26.10), typecheck clean, shipped SKILL.md regenerated. Live pass on emma@ordner.fyi with a scratch picture: set → read-back → sidecar round-trip → clear; all scratch artifacts deleted.

This pull request has no versions submitted yet.