Kickside inbox — central review/approval inbox with declarative, web-component-rendered item types and realtime updates.

BUSL-1.1 1.8k downloads
Updated 3 days ago Repository
kicksideinboxreviewapprovalsrealtime

Run

wippy run kickside/inbox

kickside/inbox

Central review/approval inbox. Action results, reminders, and notices land here for human review.

  • Items are thread events reduced by the owner inbox projection (pending -> approved | rejected | dismissed | resolved). Built-in actions use their specific status; other declared actions use resolved. There is no parallel count/log table; callers derive counts from the same projected item list.
  • Item types are declarative: a module registers a registry.entry with meta.type = inbox.item_type carrying render metadata (value, name, icon, category, component_tag, component_base_path, and optional action_owner: component). The UI maps an item's item_type to the item type's web component — adding an item type needs no inbox change. The host owns pending action controls unless the declaration explicitly assigns them to its component. Item metadata cannot change that ownership. The bundled notice item type is the base reference to copy.
  • Public meta: the projection writes component-wide renderable summary counts to component meta using flat, explicit <thing>_count fields (pending_count, item_count, resolved_count, urgent_count, high_count). pending_count is the actionable inbox badge. Do not store per-viewer unread/visited state in public meta.
  • Realtime: the projection writes public meta, and the component engine emits component.<id> / component.meta.changed. The UI uses that canonical topic as its live invalidation signal and reloads the list from /inbox; there is no parallel inbox realtime topic.
  • Resolution consistency: the canonical inbox.item_type declaration owns its allowed actions, optional side-effect-free on_validate, and optional idempotent on_resolve. Inbox invokes both callbacks synchronously under the resolver's actor and scope, and appends the one terminal item.resolved event only after they succeed. Item metadata is data, never executable binding authority. Because a callback may succeed immediately before an append fails, providers must bind the supplied stable resolution_id to exactly one action and form payload, make an identical retry idempotent, and reject a retry whose action or form differs. Inbox does not yet persist a terminal claim before invoking callbacks, so this provider contract is required. Pending items also resolve against the current live item-type declaration; declaration updates must remain backward-compatible with existing pending items until declaration version freezing is designed.
  • Workflow port: kickside.inbox.sink:inbox_item is the canonical writable destination. It is a first-class kickside.automation.port backed by kickside.inbox.sink:inbox_sink and posts to the ambient actor's inbox. Workflow builders discover it from the same ports catalog as every other sink; there is no separate inbox dropdown/catalog path.

API

| Method | Path | Purpose | |---|---|---| | GET | /inbox | list items (status, type, priority, page, limit) | | GET | /inbox/item-types | declared item types + render metadata | | POST | /inbox | enqueue an item | | POST | /inbox/{id}/{action} | apply an action declared by the item's canonical item type |

Modules can also enqueue in-process via the inbox_writer library. Terminal resolution always goes through inbox_resolve, which owns action validation and callbacks before committing the event.