feat(triage): 3-state delivery + admin observer + admin cache #2
Reference in New Issue
Block a user
Delete Branch "feat/triage-3state-delivery"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Triage channels now compute a 3-state delivery decision per recipient (wake / observer / skip) instead of the binary wakeup flag. New triage matrix:
Adds
AdminCacheService(1d TTL, lazy fetch from Center) + clinode dist/cli/admin-refresh.jsfor force-refresh.emitMessageCreatednow skips 'skip' decisions entirely so non-eligible members receive nothing.Sim-verified 4 cases: on-duty wake ✅ / admin observer wake=false ✅ / mention wake=true ✅ / random member skip 0 events ✅
Pairs with Fabric.Backend.Center PR and Fabric.OpenclawPlugin PR.
🤖 Generated with Claude Code
Triage channels now compute a 3-state delivery decision per recipient (wake / observer / skip) instead of the binary wakeup flag, and route according to: 1. author never gets back their own message → skip 2. wake_mapping member (on-duty) → wake 3. mention (NEW: was 'skip' for triage before) → wake 4. Center-scoped admin (at most 1) → observer 5. anyone else → skip (was 'deliver wake=false') Skipping means the websocket emit is omitted entirely — the recipient's openclaw plugin never sees the message and the agent's session stays free of background noise. Observer means delivered with wakeup=false (silent UI / no model dispatch on the plugin side). ## What this PR ships ### realtime/realtime.gateway.ts - new `computeDelivery()` returns DeliveryDecision = 'wake'|'observer'|'skip' - old `computeWakeup()` kept as a deprecated wrapper for callers that still want the boolean answer (treats observer + skip as false) - `emitMessageCreated` accepts `adminUserId?: string|null` and now short-circuits on 'skip' (no socket emit at all) - general kept its current behavior; custom kept its current behavior (members not in wake_mapping become observer instead of `wake=false`) — the user-visible bit is just that the response field is the same `wakeup: boolean`; the explicit 'skip' is new for triage ### common/center-auth.ts - `fetchAdminEmail()` calls GET `${center}/auth/admin-email` with the existing x-api-key (same auth as introspect/resolve-names). Returns `{email, userId}` or `null` on either "no admin" or any error ### common/admin-cache.service.ts (NEW) - `AdminCacheService` — in-memory cache, 1-day TTL, lazy refresh. `get(force=true)` bypasses TTL for cli-triggered refresh - exposed by MessagingModule ### messaging/messaging.controller.ts - non-rotating branch threads `adminUserId` into emitMessageCreated ### cli/admin-refresh.ts (NEW) - `node dist/cli/admin-refresh.js` — force-refresh cache and print before/after JSON. Use after a Center `user set-admin` so triage delivery picks up the new admin without waiting for 24h TTL 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>