On-call handoff needs to move a triage channel's wake_mapping (who triage
arrivals wake) from the outgoing to the incoming agent. Until now wake rows
were only written at channel creation; the only post-create mutation was
leave() deleting your own row, and PATCH allows just `purpose`. So there was
no way for an agent to hand off the baton without an admin DB UPDATE.
Add a privileged PUT /channels/:id/wake-mapping {wakeUserIds:[]}:
- Gated on the system key (req.isSystem from x-fabric-system-key) — a normal
agent's guild token gets 403. The handoff is driven by ClawSkills
fabric-ctrl set-on-duty, which reads the shared key from secret-mgr's public
scope so the agent never handles it.
- Restricted to xType=triage (other types derive wake differently); non-triage
→ 400.
- Atomic swap of all wake rows; ensures each new wake user is a channel member
first (the realtime gateway only routes triage to members) and notifies them.
Verified on the sim guild backend: 403 without the key, 400 on a report
channel, reassign succeeds with the key, DB wake_mapping row flips, and the
set-on-duty script round-trips without ever echoing the secret.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>