inbox
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/inboxkickside/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 useresolved. 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.entrywithmeta.type = inbox.item_typecarrying render metadata (value,name,icon,category,component_tag,component_base_path, and optionalaction_owner: component). The UI maps an item'sitem_typeto 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 bundlednoticeitem type is the base reference to copy. - Public meta: the projection writes component-wide renderable summary
counts to component meta using flat, explicit
<thing>_countfields (pending_count,item_count,resolved_count,urgent_count,high_count).pending_countis 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_typedeclaration owns its allowedactions, optional side-effect-freeon_validate, and optional idempotenton_resolve. Inbox invokes both callbacks synchronously under the resolver's actor and scope, and appends the one terminalitem.resolvedevent 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 stableresolution_idto 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_itemis the canonical writable destination. It is a first-classkickside.automation.portbacked bykickside.inbox.sink:inbox_sinkand 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.