Skip to content

UX/DX: accept human-readable channel names (not just raw IDs) for webhook + reminder notification targets #1682

Description

@Aaronontheweb

Summary

Notification / delivery targets across NetClaw's automation surfaces — inbound webhook routes and reminders — require a raw platform channel ID (e.g. a Slack channel id like C0XXXXXXXXX). Operators think in channel names (#netclaw-customer-ops), so this is recurring friction: it's easy to paste the wrong ID, and you can't eyeball a route/reminder file and tell where it delivers.

Current state (verified on a live 0.25.0-alpha.onnx.7 instance)

  • Webhook routenetclaw webhooks set … --notification-channel <id> → stored as
    notificationTarget: { "kind": "Slack", "channelId": "C0XXXXXXXXX" }.
  • Reminder — delivery is stored as
    delivery: { "kind": "Channel", "transport": "slack", "address": "C0XXXXXXXXX" }.
  • Both take the raw ID only; there's no name-based input, and the ID is what's persisted.
  • Aside worth noting: the two features model the same "where to notify" concept in different shapesnotificationTarget.channelId vs delivery.address + transport. Unifying them would be nice, but the ID friction is the primary ask here.

Proposal

Accept a human-readable channel name as input on both surfaces, resolve it to an ID at set/create time via the transport's API (the daemon already holds the token), and store the resolved ID (stable across renames). Echo the resolution back, e.g. resolved '#netclaw-customer-ops' → C0XXXXXXXXX.

Design nuance — per-transport, with graceful fallback

Name resolution only works cleanly where names are unique/unambiguous:

  • Slack — channel names are unique per workspace → #name resolves to exactly one ID. Clean.
  • Discord — allows duplicate channel names, scoped by guild → category, so a bare name is ambiguous; needs guild (and maybe category) context or a qualified form.
  • Mattermost — channel slugs are per-team; needs team context.

So the contract should be layered:

  • ID is always accepted — the universal, rename-proof input. Unchanged.
  • Name is opt-in ergonomic sugar, resolved only when unambiguous; on ambiguity, fail with the candidate list (or require a qualified form like guild/channel / team/channel).
  • Never store a bare name (it breaks on rename) — always persist the resolved ID.

This is "optional per-transport name resolution that falls back to ID," not "names instead of IDs."

Scope

  • Webhook set_webhook tool + netclaw webhooks set CLI (--notification-channel).
  • Reminder set_reminder tool + netclaw reminder create CLI (--address / --transport).
  • Resolution is per-transport, Slack first (it's the clean case); other transports keep accepting IDs / qualified forms.

Metadata

Metadata

Assignees

No one assigned

    Labels

    UX/DXUI / UX / DX friction issue or user-facing annoyances.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions